QSMM Programmer Manual

Table of Contents


Next: , Up: (dir)

QSMM Programmer Manual

This manual documents QSMM, a framework for development of intelligent systems.

This edition of the manual was updated 28 July 2014 and corresponds to release 1.16 of the framework.

You can find more information about QSMM on the project homepage, http://qsmm.org/.

Please submit bugs in this manual to a mailing list for QSMM users. Posting messages to the mailing list requires prior subscription to it. The mailing list information page is available at https://lists.sourceforge.net/lists/listinfo/qsmm-users. Using that page you can subscribe to the mailing list, unsubscribe from the list, view list archives, and do other actions.

Copyright © 2012, 2013, 2014 Oleg Volkov.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with the Invariant Sections being just “GNU General Public License”, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled “GNU Free Documentation License”.


Next: , Previous: Top, Up: Top

Acknowledgements

The author of this manual expresses thanks to Bryan Barnes for proofreading What Is Intelligence?, Spur-driven Behavior, Building Blocks for Intelligent Machines, and Animate Machines.


Next: , Previous: Acknowledgements, Up: Top

1 Introduction

QSMM is a recursive acronym for “QSMM State Machine Model.” That kind of a state machine makes possible to implement a system with intelligent behavior. The extent to which the system will be intelligent depends on basic properties of the state machine and on a specific implementation of a system that relies on it.

From the point of view of a programmer, a system he develops is represented by the state model that needs to be programmed. To promote the development, the programmer can use various function libraries and frameworks. QSMM is an intelligent state model development framework. It is a C function and macro library which by its author's belief could facilitate the development of systems with intelligent behavior.

The package source code is distributed under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation. See GNU General Public License, for the text of the License.

This manual specifically is covered by the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation. See GNU Free Documentation License, for the text of the License.


Next: , Up: Introduction

1.1 What Is Intelligence?

There are many definitions for intelligence as well as types of intelligence. Most definitions include lists of activities inherent to humans, such as reasoning, planning, solving problems, thinking abstractly, comprehending complex ideas, learning from experience, and adapting effectively to the environment. When attempting to develop intelligent machines, researchers try to make them perform a subset of those activities with as high a level of plausibility as possible. However, from the standpoint of knowledge formalization and for a more precise formulation of research goals, it would be useful to point out the smallest subset of activities, which when performed by an animate being or inanimate machine, can be considered as an indication that it is intelligent enough.

First, it is necessary to mention that intelligence implies a goal (in the “Handbook of Human Intelligence” Sternberg and Salter defined intelligence as “goal-directed adaptive behavior”). In the case of animate being, the goal is surviving, in the case of a machine created by humans the goal is solving tasks assigned by humans. Animals show their basic intelligence, e.g. when they adapt to the environment, seek for food, and build nests. One can say today's machines show their intelligence by effectively doing tasks they are intended to do. This is not a too distorted interpretation of intelligence if we review the book “Symbolic Logic and Intelligent Machines” of Edmund C. Berkeley published in 1959, where the author called intelligent machines the electromechanical arrangements, which solve problems that involve logic.

The advanced level of intelligence can be defined as the production and use of work tools. As it has turned out, the production and use of work tools is peculiar not only to humans, but also to chimpanzees and to a number of other animals. For example, chimpanzees are able to find stones of appropriate weights and sizes and crack nuts using them. Another kind of activity using tools that chimpanzees are able to do is find long and thin sticks to kill small rodents, which live in the trunks of a certain species of trees, and extract them for food. The chimpanzees not only find ready-to-use work tools in nature, but they can also produce them. For example, chimpanzees can construct arrangements consisting of small tree branches and put them into termite mounds to eat termites that crawled on them. Another notable example is an experiment conducted on a pygmy chimpanzee when he was successfully trained to manufacture a sharp stone tool to cut the string, which opens the door of the trap and gives him access to sweets.

In the digital world supported by computers, work tools are computer programs that help to solve various tasks. That is, in the computer environment, the advanced level of intelligence is closely connected with a capability of synthesis and use of algorithms. A machine with an advanced level of intelligence must be capable of automated synthesis of algorithms in some form or fashion. Such machines could then internally generate a computer program, possibly consisting of a set of subroutines, which solves a task assigned by a human.

There does exist a higher level of intelligence, which is peculiar only to humans and not to chimpanzees that we will call an expert level of intelligence. Indeed, in what manner do humans and chimpanzees differ in essence if they both can produce and use work tools? One can say humans perform their activity consciously and chimpanzees do not posses that degree of consciousness. Such explanations are worthless from both scientific and practical viewpoints because when a chimpanzee does something, he can consider that he does it with the full consciousness and awareness of what he does and why.

In the book “The complete idiot's guide to Human Prehistory,” its author Robert J. Meier carefully pointed out an important fact about the production and use of burins. Such specially sharpened stones archaeologists find during excavations that correspond to the time, starting from which, tentatively speaking, creatures that have been living on Earth could be called humans. Burins are tools created for manufacturing other tools. Chimpanzees do not make tools for making other tools. That is, the expert level of intelligence peculiar only to humans consists of the ability to organize processing chains in which one kind of work tool is used to produce other kinds of work tools. The established concept for this is “production of the means of production.”

In the computer environment, programmers create such tools as compilers, various operating, execution, and development environments that help to create other computer programs. A machine with an expert level of intelligence created by humans would be one that is capable of the automated synthesis of algorithms, which the machine will use to synthesize other algorithms to solve problems assigned by humans more efficiently. Creation of such a machine is a quite specific goal that researchers could try to achieve.


Next: , Previous: What Is Intelligence?, Up: Introduction

1.2 Spur-driven Behavior

The basic problem one needs to solve when creating an intelligent machine that performs automated synthesis of algorithms, is discovering a way or manner in which a job is assigned to the machine. There are sophisticated approaches to solving this problem, e.g. by creating specifications that consist of rules and constraints. The QSMM framework uses the simplest approach, in which a developer must reduce the task to an optimization task, which goal is the maximization of increment or decrement velocities of a set of numeric quantities. Those numeric quantities could be logarithms of probabilities that should be maximized, energy values that should be minimized, or the sum of incentives, which are given to the machine when a partially designed algorithm successfully reaches some point in solving the task. The machine then has to synthesize by trial and error an algorithm, which when executed, solves the optimization task as best as possible. The classical approach to such synthesis is to use a genetic algorithm that evolves a target algorithm by multiple iterations to maximize the value of a fitness function. The QSMM framework uses a different and generally much more effective approach, where the synthesis of an algorithm is performed simultaneously with its execution.

Historically, in the QSMM framework, the value of a numeric quantity, which increment or decrement velocity is maximized, is called a spur. Every such numeric quantity specifies a spur type, to which the values of spur belong. To increase the efficiency of an algorithm synthesis, a programmer should provide a transparent way to evaluate spur values, which then allows an intelligent machine to better understand the correlation between changes made in the algorithm being synthesized and changes in spur. Developing a proper way of spur evaluation is one of the most complex tasks needed to be solved when creating an intelligent machine using the QSMM framework.


Next: , Previous: Spur-driven Behavior, Up: Introduction

1.3 Building Blocks for Intelligent Machines

In the QSMM framework, a generic building block for creating machines capable of goal-directed adaptive behavior, which implies automated synthesis of algorithms for achieving a goal, is historically called an optimal action generation engine or an actor for short. One can think of an actor as a set of neurons, possibly very large one. An actor has a number of adjustable modes, parameters, supports limited customization of its algorithms, and actually implements a probabilistic mapping—a function that ambiguously maps an argument from a set of possible arguments to a result from a set of possible outcomes. Such function, when invoked one time, can return one result, and when invoked another time, can return yet another result for the same argument.

Behavior of probabilistic mapping is modulated by spur. Supposing a result returned by a probabilistic mapping for its specific argument somehow affects spur increment velocities, the function of the atomic building block of an intelligent machine that corresponds to a particular argument of the probabilistic mapping will be returning more often a result of the mapping that maximizes those spur increment velocities. An intelligent machine might include various superpositions of probabilistic mappings, its inputs and outputs, and those superpositions would specify an adaptive generic state model aimed to solve general or specific problems.

An important concept related to the possibility of using a result of a probabilistic mapping (directly or after transforming) as an argument (or as its part) of that probabilistic mapping is state—a variable that changes its value based on its previous value. A machine typically uses various kinds of states that can be interlinked in different ways and make up its whole integral state. An integral state of the machine corresponds to a point in solving an assigned task. If to solve an assigned task the machine has to interact with the environment, then the integral state will somehow link with a state of the environment. By implementing probabilistic mappings, the QSMM framework provides the means for goal-directed tracking of the current environment state which may be needed to know when producing goal-directed adaptive behavior.

If a machine executes an algorithm, possibly in the process of synthesizing of that algorithm, then the integral state of the machine will be in some way linked with a state of the algorithm, which was put in the algorithm by its developer. Principal states of algorithms are better investigated by example of deterministic finite automatons, which representation in a programming language could correspond to assembler programs with specially organized instruction sets. Those instruction sets include: (1) custom instructions, which perform effective work and can return an outcome from a set of possible outcomes; (2) conditional jump instructions, which transfer control to a custom instruction at a specific location in the program on the basis of an outcome returned by a previously invoked custom instruction; (3) simple jump instructions that transfer control to a custom instruction at a specific location in the program unconditionally. In such assembler programs, principal states of the algorithm are custom instructions and jump instructions specify mappings between those states. An argument of mapping is a superposition of a prior principal state and outcome of a custom instruction invoked in that state. The result of mapping is a new principal state. By using probabilistic mappings supported by the QSMM framework instead of hard-coded deterministic mappings, a researcher can develop adaptive assembler programs that produce goal-directed behavior.

As one may realize, adaptive assembler programs are related to automated synthesis of algorithms. The QSMM framework provides the means to work with such assembler programs that include converting the text of a fuzzily specified assembler program to a system based on one or two probabilistic mappings, executing the assembler program with simultaneous adjusting jump probabilities to solve an assigned task more efficiently, and converting a resulting assembler program back to a text representation.

Elaboration of the concept of environment for synthesis and execution of assembler programs is a multinode environment in which different nodes can contain different assembler programs. In the QSMM framework, node means a callable state submodel. An assembler program of one node, when being executed, can call another node, i.e. transfer control to that node to execute its assembler program and then transfer control back after finishing execution of the latter program. Nodes correspond to subroutines, which synthesis and execution a researcher could relate to setting up processing chains with production and the use of different work tools.

As a helpful add-on for developing intelligent machines by means of C programming language, the QSMM framework provides an API for exchanging data packets in multithreaded programs, which can be used to simplify communication between different parts of your application, and a C implementation of functionality of STL map and multimap templates, which can be used to create mapping objects in C programs without the need to rewrite those programs in C++ and, therefore, increase their complexity.


Next: , Previous: Building Blocks for Intelligent Machines, Up: Introduction

1.4 Animate Machines

Implementation of adaptive probabilistic mapping requires providing a method of selection of a result of the mapping for an argument of the mapping. This method could be based on a deterministic process, which is deterministic changing the integral state of a system that contains superpositions of deterministic mappings, inputs, outputs, and memory variables. This process is the way today's computers operate. The method might require the use of pseudorandom number generators which operation is also based on deterministic processes. However, e.g. when creating implementations of adaptive probabilistic mappings in the form of electric circuits, it can become practically reasonable to use stochastic physical processes as a source of randomness. One can think of such a stochastic physical process as changing the integral state of a machine, which implementation uses probabilistic mappings provided by nature.

Suppose an intelligent machine, which uses probabilistic mappings provided by nature, solves an optimization task assigned by humans, which consists in the maximization of increment velocity of a numeric quantity. Consider a situation that, at a particular point of time, the increment velocity has gone down, so the machine needs to change its own behavior to increase the increment velocity even more. In other words, the machine is now in a difficult situation that means an increase of complexity of choices the machine has to perform to increase the increment velocity. The complexity of choice for a stochastic act, i.e. calling a nature-provided probabilistic mapping for a particular value of its argument, depends on how many possible distinguished outcomes the act has, which might affect how the stochastic act changes the entropy of nature.

One could relate the abstract idea of good and bad to the concept of choice complexity. The good would support a certain level of choice complexity. The survival of an animate being would be to preserve its ability to perform choices with a sufficient degree of complexity. This has something in common with the definition of life as a characteristic, which distinguishes objects that have signaling and self-sustaining processes from those that do not1. In our case, the signaling is the way of exchanging information within superpositions of probabilistic mappings, and the basic self-sustaining process is supporting certain level of choice complexity.


Next: , Previous: Animate Machines, Up: Introduction

1.5 System Requirements

Although the package source code can be adapted for use with many of available operating systems and compilers, the GNU/Linux system and GCC is a preferable combination to build the library and accompanying programs. The source code supports 32-bit and 64-bit build modes. The library had been tested to build and pass tests on MS Windows in Cygwin and MinGW/MSYS environments (a modern version of MinGW/MSYS environment should be used).

By default, the package uses a pseudorandom number generator implemented by function rand from the standard C library, which has the following drawbacks: (1) it is usually a very simple function, not intended to pass serious statistical tests for pseudorandom number generators; (2) streams of pseudorandom numbers returned by that function may vary on different platforms; (3) multiple instances of a pseudorandom number generator implemented by that function are only imitated and cannot be seeded separately. To solve these issues, the package can be configured to use a high-quality pseudorandom number generator provided by the GNU Scientific Library. There are no special requirements for a version of that library to use with the package—all recent versions can be used. QSMM starting from version 1.15 can also utilize a user-supplied random number generator, e.g. implemented by a developer by hand or which is a wrapper for a random number generator provided by another library.

The package uses the exponent function to compute relative probabilities of signal emitting. The absolute value of the argument of the exponent function sometimes can be big enough, which may cause the floating-point overflow (which yields infinity) or underflow (which yields 0). To minimize the number of such overflows and underflows during program execution, long double version of the exponent function expl is used instead of ordinary function exp on platforms where function expl and macro isfinite are available in the standard C library. If either or both of them are unavailable, then sporadic floating-point overflows and underflows shall not substantially decrease program efficiency.

However, if one considers that ordinary double precision is not sufficient, or if he makes modifications to the algorithm and overflows or underflows occur frequently even when long double precision is used, then the package can be configured to use MPFR and GNU MP libraries to solve these issues (former library depends on latter). There are no special requirements for a version of the MPFR library to use with the package—all recent versions can be used. A version of the GNU MP library should be used, which is compatible with a version of the MPFR library; see the documentation for the MPFR library for more information.

A few example programs included in the package distribution use the Curses library. On the GNU/Linux system and Cygwin it is the ncurses library. On MinGW/MSYS it is the pdcurses library. If you do not have the corresponding library on your system, the package can be configured to bypass building example programs that use it.

A number of example programs included in the package distribution use the POSIX threads API. On MinGW/MSYS building those example programs requires the use of an additional library that implements the API. If necessary, the package can be configured not to build the example programs.


Next: , Previous: System Requirements, Up: Introduction

1.6 Obtaining QSMM

The official homepage of the QSMM project is

     http://qsmm.org/

Additional information on the project is contained in a set of standard pages for a project hosted at SourceForge.net, which can be accessed via

     http://sourceforge.net/projects/qsmm/

The package distribution can be downloaded from the project files page provided by SourceForge.net at

     http://sourceforge.net/projects/qsmm/files/


Next: , Previous: Obtaining QSMM, Up: Introduction

1.7 Reporting Bugs and Getting Help

It should be said that algorithms, which solve optimization tasks with moderate efficiency, can be automatically synthesized from scratch using the QSMM framework only for a small subset of tasks. To increase efficiency, the developer has to provide a proper template for a synthesized algorithm in the form of a generic state model using a C program and/or as a fuzzily specified assembler program with a custom instruction set. The developer may also improve algorithms implemented in the QSMM framework or use them in more efficient modes, which were unforeseen by the package author; in some cases that can be done without changing and recompiling the package source code.

Besides limitations caused by imperfection of algorithms used, the package may have bugs. To facilitate quicker fixing bugs in the package and the package documentation, please report them to a mailing list for QSMM package users. See below for details about accessing the mailing list.

If you want a new feature to appear in the package, e.g. a new API function, without which it is hard or even impossible to write some kind of applications, then please submit that to a mailing list for QSMM package users too.

Package users are encouraged to ask questions, ask for technical support, which indeed may require fixing implicit bugs in the package, and inform the author of real examples of package use via the mailing list. That kind of activity will hopefully improve the package. The mailing list information page is available at

     https://lists.sourceforge.net/lists/listinfo/qsmm-users

Using that page you can subscribe to the mailing list, unsubscribe from the list, view list archives, and do other actions.

Software described in this manual is provided “as is,” in the hope that it will be useful, but without any warranty. See GNU General Public License, for more details.


Next: , Previous: Reporting Bugs and Getting Help, Up: Introduction

1.8 Installation

For package installation instructions, please see file INSTALL at the root of the package distribution. Those instructions are not provided in this manual to not to duplicate the same text in two documentation files. At the same time, instructions given in file INSTALL can be used to build other documentation files, including this manual.


Next: , Previous: Installation, Up: Introduction

1.9 Header Files

Command make install installs QSMM header files to subdirectory qsmm in a directory for C header files set by the configure script, usually /usr/local/include/ or /usr/include/. Below there are the list and the description of header files being installed.

qsmm.h
The main header file of the framework. Contains most of datatype, function, and macro definitions, which may be needed by a developer.
internal.h
A header file that contains an API used internally by the framework. The API was documented in previous versions of the framework, but is undocumented in this version to not to overcomplicate the manual with excessive details. That API could not be made invisible outside of the library because of the need to preserve backward compatibility. The preprocessor #include directive includes the header file in header file qsmm.h.
side.h
A self-contained header file for the Side API. That simple API provides means for exchanging data packets, especially signals, between threads in a multithreaded program. In some cases, program structure with a number of interacting sides, which execute in separate threads and exchange signals, can simplify program development or experimenting. Command make install installs the header file when the package is configured to use the POSIX threads API (see file INSTALL at the root of the package distribution, for information on package configuring).
map.h
A self-contained header file for the C implementation of functionality of STL map and multimap templates.
version.h
A header file with macro QSMM_HEADERS_VERSION defined to a version of the package. That macro could be used to check whether versions of the headers and the library conform. The preprocessor #include directive includes the header file in header file qsmm.h. The contents of version.h are generated using template qsmm/version.h.in in the package distribution.

When you include these header files in a program with a preprocessor #include directive, you should specify directory prefix qsmm. For example, to include header file qsmm.h, you should write

     #include <qsmm/qsmm.h>

C functions declared in the header files are wrapped in extern "C" declarations, so when a C++ source file includes these header files, correct linkage is provided with functions contained in the QSMM library.


Next: , Previous: Header Files, Up: Introduction

1.10 Linking with the Library

When linking with a shared version of the QSMM library, dependencies on other libraries that may be used by the QSMM library are compiled in the file of shared library, and you do not need to specify them in a link command. (Those dependencies are defined at a package configuring phase when you execute the configure script.) An example command to link with a shared version of the library is

     gcc example.o -L/usr/local/lib -lqsmm

You need to specify option -L/usr/local/lib if the library is installed in directory /usr/local/lib/ and that directory is not on standard search path of your linker.

When linking with a static version of the library, in a link command you should also specify libraries the QSMM library depends on using such linker options as -lgsl, -lmpfr, -lgmp, and -lm. An example command to produce a fully statically linked executable is

     gcc -static example.o -lqsmm -lgsl -lm

Alternatively, you can link via command libtool, which uses file libqsmm.la copied to the library directory when installing the package. That file describes all necessary dependencies needed to produce an executable linked against the library, which are useful in case of static linking.


Next: , Previous: Linking with the Library, Up: Introduction

1.11 Getting the Version of the Library

Currently, the version of the QSMM library is specified in major.minor format. In the future, versions with greater major number may require considerable backward-incompatible changes at source code level for programs that use the library.

To get a string representation of the version, the following API function from the qsmm.h header can be used.

— Function: const char * qsmm_version ()

This function returns the version of the QSMM library as a string in major.minor format, e.g. ‘1.16’.

To examine the version of QSMM library headers, which were used when compiling a program, the following macro from the version.h header included by the preprocessor #include directive in the qsmm.h header can be used.

— Macro: QSMM_HEADERS_VERSION

This macro is defined to a string representation of the version of QSMM library headers installed, e.g. ‘1.16’, which normally should be equal to the version of a QSMM library used when linking the program.


Next: , Previous: Getting the Version of the Library, Up: Introduction

1.12 Object Handles

In the QSMM API, to refer to various objects handles are used. A handle is an opaque typed pointer to a QSMM internal structure, the contents of which cannot be examined or changed by an application program, except via API functions that take a handle of that type as an argument. The type of a handle corresponds to the type of an object the handle refers to. Dereferencing handles is useless, as they all have incomplete types. Because a handle is a pointer, it can have the NULL value. The table below lists handle types used in the QSMM API, corresponding object types, and references to sections in this manual with detailed descriptions of those handle types.

Handle Type Object Type Reference to a Section in this Manual


qsmm_t multinode model Creating a Multinode Model.


qsmm_actor_t actor Basic Datatypes and Macros.


qsmm_actpair_t actor pair Creating the Model Instance.


qsmm_instr_t assembler instruction Basic Datatypes.


qsmm_iter_t map iterator Part of the C implementation of functionality of STL map and multimap templates. See Creating Maps and Iterators.


qsmm_map_t map Part of the C implementation of functionality of STL map and multimap templates. See Creating Maps and Iterators.


qsmm_msg_t message Creating Messages.


qsmm_msglist_t message list Creating a Message List.


qsmm_prg_t assembler program Basic Datatypes.


qsmm_rng_t random number generator Creating a Random Number Generator.


qsmm_side_t interaction side Part of the Side API. See Registering Interaction Sides.


qsmm_storage_t statistics storage Types of Storage.


qsmm_vec_t vector Ordinary and Sparse Vectors.

In a multithreaded program, it is generally acceptable to use different handles in different threads concurrently on condition that the handles are not interrelated. The handles are interrelated e.g. in a situation when one handle refers to a component of an object referred to by another handle.


Previous: Object Handles, Up: Introduction

1.13 Error Handling

If a QSMM API function returns a value of type int, then it can be an error code. All error codes are negative. Zero and positive return values may indicate additional information regarding successful completion of a function call. If a QSMM API function does not return a value of type int, it may not return an error code, because error condition cannot arise within the function, or at least this was assumed when the function had first appeared in the QSMM API.

If not otherwise noted in the description of an error code of a QSMM API function, returning the error code by the function means the system state remains unchanged, i.e. just after calling the function the system is in the same state as just before calling the function.

To get the textual description of an error code, the following API function from the qsmm.h header can be used.

— Function: const char * qsmm_err_str (int err)

This function returns the textual description of QSMM API error code err. For invalid error codes, string "(invalid error code)" is returned.

When you use the QSMM package for creating multinode models, you can associate an error handler with a model. In this case, the error handler may receive extended information on an error occurred. The use of the error handler may eliminate the need to check return codes of calls to QSMM API functions, which take argument qsmm_t, to determine whether any of these functions has failed. See Error Handling for a Multinode Model, for information on using error handlers.


Next: , Previous: Introduction, Up: Top

2 Optimal Action Generation Engine

An optimal action generation engine, called an actor for short, interacts with an application program or the environment by means of exchanging signals. The actor emits output signals (or action signals) on the basis of input signals supplied to it. A list of occurrences of input and output signals with particular moments of time when those occurrences took place represents the event history of the actor.

The actor generates output signals according to spur increments, which belong to one or more spur types and are supplied to it along with input signals. See Spur-driven Behavior, for definitions of spur and spur type.

To every spur type there correspond a way of spur perception, spur weight, and a type of time used to compute spur increment velocity. A list of definitions of spur types used by the actor specifies its spur scheme.


Next: , Up: Optimal Action Generation Engine

2.1 Event History Example

An example of event history is shown in evt_hist. Filled dots denote input signals, and unfilled dots denote output signals. Above each dot, a signal identifier is indicated. In the example event history pattern “input signal–input signal–input signal–output signal” is used, but in your programs you can use other patterns. Values below the time line indicate spur increments that take place between receiving input signals.

To make a description of actor operating principles clearer, a single spur type is used in the example. The spur type has a normal way of spur perception, weight 1, and a continuous type of time used to compute spur increment velocity. For that spur type, the goal of actor operation is to emit output signals to increase the value of the spur as quicker as possible.

An example of event history

Figure 2.1: an example of event history

An actor state, represented by a list of signal identifiers of fixed length, on the basis of which the actor chooses an output signal, is called an action choice state. Typically, but not always, it is a known current system or environment state, for which it is needed to produce an optimal action. In the example it is indicated that an action choice state is a list of identifiers of input signals, which are received by the actor between emitting output signals. An action choice state, denoted in the figure by a horizontal square bracket, is represented by list <1, 2, 5>. In some cases, it might be necessary to include in an action choice state the last output signal emitted. Thus, an example action choice state would be represented by list <7, 1, 2, 5>.

The actor generates output signals stochastically. Probability of emitting a specific output signal by the actor when producing an action is calculated using statistics collected over the event history. The actor keeps track of output signals emitted in action choice states since the beginning of its operation. It emits more often output signals, which give higher spur increment velocities calculated over periods of time passed between the moment when an action choice state has been active and the next moment the same action choice state is active again2.

For example, let us suppose that the actor has recorded that output signal 9 emitted in action choice state <1, 2, 5> provides spur increment +100 over a period of 20 units until the actor registers action choice state <1, 2, 5> in the event history again. Let us assume that the actor has also recorded that output signal 8 emitted in action choice state <1, 2, 5> provides spur increment +120 over a period of 15 units until the actor registers action choice state <1, 2, 5> the next time in the event history. Therefore, when generating future actions for action choice state <1, 2, 5>, the actor chooses signal 8 with higher probability than signal 9, because the former one has greater spur increment velocity3. When the actor emits the same output signal in the same action choice state more than once, the statistics is accumulated, and mean spur increment velocity is used to calculate the probability of output signal choice.

Specific moments of time, when the actor emits output signals, are determined by an application program that uses the actor. That is, for example, for action choice state <1, 2, 5> an Actor API function did return to the application program information that the actor considers it is optimal to emit output signal 8. However, particular moment of time, when the actor emits that output signal, has to be chosen by the application program.


Next: , Previous: Event History Example, Up: Optimal Action Generation Engine

2.2 Small and Large Actors

The most time-consuming operation, which usually has to be frequently performed by an actor, is stochastic emitting an optimal output signal. The only actor type that was implemented in QSMM before version 1.15 is a small actor. The small actor performs the operation of stochastic emitting an optimal output signal in the following steps.

  1. Relative probabilities of all output signals, which could be emitted, are calculated.
  2. An optimal output signal to emit is chosen stochastically according to those relative probabilities using a random number generator.

From the standpoint of computer implementation, the most time-consuming is the first step when the actor calculates relative probabilities of all possible output signals. Calculating every relative probability requires access to statistics storage, which is why it is time-expensive. For example, to choose an optimal output signal from a set of 16 output signals, 16 evaluations of a function, which returns a relative probability of signal emitting, have to be performed by the small actor. This situation is illustrated in actor_small.

Emitting an output signal in an action choice state of small actor

Figure 2.2: emitting an output signal in an action choice state of small actor

To speed up emitting output signals by an actor, in QSMM version 1.15, there had been introduced the concept of large actor. A large actor provides fast stochastic selection of the output signal when the number of those signals is large or even huge. The only limitation is the amount of available memory for storing control structures of the large actor.

Efficiency in solving a problem by a large actor (as well as by small one) depends on a function that returns a relative probability of signal emitting. The default function used by a large actor can provide moderate efficiency in solving certain kinds of problems, among which the identification of current environment state. Those developers, who are unsatisfied with results produced using that function, can supply a custom function via corresponding API calls.

Fast stochastic choice of output signals by a large actor is supported by using a tree that contains nodes, which are controlled by a small actor. With every tree node, the following entities are associated.

  1. An action choice state: either the root one, for which an optimal output signal has to be generated, or an intermediate one.
  2. A small array of relative probabilities of output signals (that typically contains a few elements), which are either output signals of the large actor or intermediate output signals. To every intermediate output signal there corresponds an intermediate action choice state, which is located at a deeper hierarchy level.

Tree structure is illustrated in actor_large, where indexed letters “s” represent intermediate action choice states, and indexed letters “o” represent intermediate output signals.

Emitting an output signal in an action choice state of large actor

Figure 2.3: emitting an output signal in an action choice state of large actor

A large actor performs the operation of stochastic emitting an optimal output signal by the following algorithm.

  1. Assign an action choice state, for which an optimal output signal has to be generated by the large actor, to current intermediate action choice state.
  2. Stochastically produce an optimal output signal for current intermediate action choice state by a small actor associated with the large actor. This operation requires calculating relative probabilities of all output signals (typically a few ones) that correspond to current intermediate action choice state.
  3. An output signal generated at step 2 can be either an intermediate output signal or an output signal of the large actor. If the output signal is the one of the large actor, then finish.
  4. Change current intermediate action choice state to one that corresponds to the intermediate output signal and go to step 2.

Thus, using a tree, represented in Figure 2.3, to choose an optimal output signal from a set of 16 output signals, only 8 evaluations of a function, which returns a relative probability of signal emitting, are necessary. A taller binary tree provides the selection of optimal output signal from a set of 256 output signals using 16 evaluations of the function.

The structure of the tree affects the weights (or, in other words, the relative profile probabilities) of output signals of a large actor and the speed of emitting those signals by the actor. Large actors use n-ary Huffman trees to stochastically produce optimal output signals. By using a Huffman tree built for a given list of output signal weights, emitting output signals, which have greater weights, requires the same or smaller number of evaluations of relative probability function compared with emitting output signals that have lesser weights.

Although having much the same API, small and large actors are not fully interchangeable. There are specifics of using actors of every type.


Next: , Previous: Small and Large Actors, Up: Optimal Action Generation Engine

2.3 Basic Datatypes and Macros

A small or a large actor is referred to by an actor handle.

— Data type: qsmm_actor_t

This is a type for an actor handle. It is a pointer, so variables of this type can have zero value. Function qsmm_actor_create allocates a new actor handle. Function qsmm_actor_destroy frees an existing actor handle. After allocating an actor handle, it can be passed to API functions that take argument qsmm_actor_t until the handle is freed.

A special datatype exists for signal identifiers.

— Data type: qsmm_sig_t

A datatype for storing signal identifiers defined as unsigned int. However, the supported number of signals cannot exceed the maximum value for the int type because of the way of how the library source code is written. The type could be changed to unsigned short to reduce the amount of memory needed to store statistics collected over the event history.

The following macros are associated with a datatype for signal identifiers.

— Macro: QSMM_SIG_INVALID

Represents an invalid signal identifier. It is often used as a substitute for the NULL value of signal identifier, because identifier 0 is a valid one. Is defined as ((qsmm_sig_t) -1).

— Macro: QSMM_SIG_MAX

Represents the maximum possible value of signal identifier. Currently, for identifiers of the unsigned int type is defined to the maximum value of the int type. For identifiers of the unsigned short type, this macro could be defined as (QSMM_SIG_INVALID-1).


Next: , Previous: Basic Datatypes and Macros, Up: Optimal Action Generation Engine

2.4 Creating an Actor

To create and destroy an actor, the following functions can be used.

— Function: int qsmm_actor_create (const struct qsmm_actor_desc_s *desc_p, qsmm_actor_t *actor_p)

This function creates an actor using parameters specified in *desc_p and stores a newly allocated actor handle in *actor_p.

The function returns a non-negative value on success or a negative error code on failure in creating an actor. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Either argument actor_p is 0 or parameters specified in *desc_p are invalid.
QSMM_ERR_NOMEM
There was not enough memory to create an actor.
QSMM_ERR_BIGMDL
A large actor is to be created, but a multinode model, which shall provide operation of the actor, is too complicated. See Creating the Model Instance, for a description of error QSMM_ERR_BIGMDL which can be raised by function qsmm_engine_create when creating the multinode model instance and which causes function qsmm_actor_create to return this error code.

— Function: void qsmm_actor_destroy (qsmm_actor_t actor)

This function destroys an actor specified by actor handle actor. After actor destruction, the actor handle must not be used. If actor is 0, then the function will do nothing.

A structure, a pointer to which is an argument of function qsmm_actor_create, along with an associated datatype, a union, and enclosed structures is described below.

— Structure: qsmm_actor_desc_s

This structure describes parameters for creating an actor. It contains the following fields.

— Field: char use_flat_storage

A flag that specifies whether the actor should use flat storage or map storage for collecting statistics on the event history. If the value of this field is non-zero and the value of field large_desc_p is zero, then flat storage will be used. If the value of this field is non-zero and the value of field large_desc_p is non-zero, then error QSMM_ERR_INVAL will be reported. If the value of this field is zero, then map storage will be used.

Flat storage is a preallocated storage presumably of big size, but with quick access to data elements. Map storage is a dynamically allocated storage presumably of lesser size, but with slower access to data elements; binary trees are used for backing storage. See Types of Storage, for additional information on statistics storage types.

— Field: int nspur

The number of spur types used by the actor. Must be greater than 0. If the actor is the large one, then a small actor, which is associated with the large actor, will use 1+nspur spur types, where an implicitly added spur type with the lowest index corresponds to the automatic spur of the small actor. See Automatic Spur, for more information on that spur.

— Field: int ngram_sz

The length of a list of signal identifiers, which defines an action choice state. Must be greater than 0. The longer the list is the more events in the event history are typically needed to train the actor.

— Field: int profile_pool_sz

A non-negative size of the pool of probabilities lists in normal form that could be prepared and then set for particular action choice state n-grams. For a small actor, it is the maximum number of unique sorted lists of output signal probabilities, which could be stored in the actor's memory. For a large actor, it is the maximum number of Huffman trees of unique structure, which could be stored in the actor's memory (not counting an automatically created default Huffman tree that corresponds to case when profile probabilities of all output signals are equal). To determine whether a Huffman tree has to be reused, a temporary Huffman tree could be created, which is why the value of this field should generally be greater by 1 than the number of Huffman trees of unique structure required to represent sorted lists of output signal probabilities.

— Field: int compat

The compatibility level of algorithms used by the actor. Must be 0 or 1. Value 0 means to use original algorithms that were implemented in QSMM before version 1.15. Value 1 means to use enhanced algorithms:

  • use a simpler formula for automatic spur increment;
  • in a formula, which gives a relative probability of output signal choice, additionally multiply the mean discrete cycle period by a value returned by function qsmm_get_actor_naction_per_evt and possibly by 2.

Setting a value of this field to 1 is recommended for your new programs. This manual does not include outdated details specific to the original algorithms.

— Field: double sparse_fill_max

The maximum fill ratio for vectors that hold relative probabilities of output signals choice, on the basis of which the actor decides when to use sparse vectors instead of ordinary vectors. Must be a number between 0 and 1 (inclusive). Value 0 indicates that the actor must always use ordinary vectors. Value 1 indicates that the actor must always use sparse vectors. Values between 0 and 1 indicate the maximum percentage (divided by 100) of non-zero elements in sparse vectors relative to the number of vector dimensions.

Note: when a large actor is created (i.e. field large_desc_p has a non-zero value), forgetting to set field sparse_fill_max to a positive value, e.g. to 0.2, will cause bad actor performance.

— Field: qsmm_rng_t rng

The handle of a random number generator to be used by the actor. See Random Number Generators, for information on how to create, destroy random number generators, and perform other operations on them. Can be 0, in which case an instance of default random number generator is automatically created for use by the actor and destroyed upon actor destruction.

— Field: enum qsmm_actor_sig_spec_e sig_spec_type

The type of specification of the number and directions of signals used by the actor. Here “signal direction” means whether a signal is input one, output one, or belongs to both types.

Value QSMM_ACTOR_SIG_SPEC_IN_OUT means that the number of input signals and the number of output signals are specified in field sig_spec of this structure.

Value QSMM_ACTOR_SIG_SPEC_MASK means that the total number of signals and a mask, which describes directions of those signals, are specified in field sig_spec of this structure.

— Field: union qsmm_actor_sig_spec_u sig_spec

A specification of the number and direction of signals used by the actor.

If field sig_spec_type of this structure has value QSMM_ACTOR_SIG_SPEC_IN_OUT, then the specification of the number and direction of signals must be stored in sig_spec.in_out.

If field sig_spec_type of this structure has value QSMM_ACTOR_SIG_SPEC_MASK, then the specification of the number and direction of signals must be stored in sig_spec.mask.

— Field: struct qsmm_pair_sig_s * range_sig_p

Ranges of signal identifiers in a list that defines an action choice state. Those ranges are used to check the validity of a list of signal identifiers, which defines current action choice state, in various Actor API functions, to reduce the memory footprint of flat storage if a small actor uses such storage, and to reduce the number of nodes in a multinode model that corresponds to a large actor. In future versions of the package, the ranges can be used for other purposes. To reduce the memory footprint of the actor, it is recommended to specify the ranges as precisely as possible.

If this field has a non-zero value, then it must be a pointer to an array of ngram_sz elements, which correspond to elements of a list that defines an action choice state, where each element is a pair. Field first of a pair defines the minimum value of signal identifier and field second of a pair defines the maximum value of signal identifier. The value of first must be less than or equal to the value of second, and the value of second must be less than the total number of signals of the actor specified using fields sig_spec_type and sig_spec of this structure. If field range_sig_p has zero value, then it will be assumed that every signal in the list, which defines an action choice state, can be in the range 0 to the total number of signals of the actor (exclusive).

— Field: struct qsmm_actor_large_desc_s * large_desc_p

Parameters of large actor. A non-zero value of this field indicates to create a large actor.

To improve compatibility with future versions of the library, an instance of structure qsmm_actor_desc_s, a pointer to which is passed to function qsmm_actor_create, should be zeroed using function memset before setting values of structure fields.

— Enumeration: qsmm_actor_sig_spec_e

This enumeration specifies how the number and direction of signals of an actor are stored in field sig_spec of structure qsmm_actor_desc_s. The enumeration contains the following elements.

QSMM_ACTOR_SIG_SPEC_IN_OUT
The actor has specified number of input signals and specified number of output signals. A segment of output signal identifiers follows a segment of input signal identifiers. For example, if the actor has 6 input signals and 4 output signals, then signals 0, 1, 2, 3, 4, 5 will be input ones, and signals 6, 7, 8, 9 will be output ones. The numbers of input and output signals should be stored in field sig_spec.in_out of structure qsmm_actor_desc_s.
QSMM_ACTOR_SIG_SPEC_MASK
The actor has specified total number of signals. Every signal can be input one. A mask is given that describes which of those signals are also output ones. The total number of signals and the mask should be stored in field sig_spec.mask of structure qsmm_actor_desc_s.

— Union: qsmm_actor_sig_spec_u

This union describes in two possible forms the number and direction of signals of an actor. Field sig_spec of structure qsmm_actor_desc_s holds that information. The union contains the following fields.

— Field: struct qsmm_actor_sig_spec_in_out_s in_out

The number of input signals and the number of output signals. This field is used when field sig_spec_type of structure qsmm_actor_desc_s has value QSMM_ACTOR_SIG_SPEC_IN_OUT.

— Field: struct qsmm_actor_sig_spec_mask_s mask

The total number of signals and a subset of output signals. This field is used when field sig_spec_type of structure qsmm_actor_desc_s has value QSMM_ACTOR_SIG_SPEC_MASK.

— Structure: qsmm_actor_sig_spec_in_out_s

This structure specifies the number of input signals and the number of output signals of an actor. It contains the following fields.

— Field: int nsig_in

The number of input signals of the actor. Must be greater than 0.

— Field: int nsig_out

The number of output signals of the actor. Must be greater than 1.

The sum of values in fields nsig_in and nsig_out must be less than or equal to QSMM_SIG_MAX+1.

— Structure: qsmm_actor_sig_spec_mask_s

This structure specifies the total number of signals and a subset of output signals of an actor. It contains the following fields.

— Field: char * is_sig_ctrl_p

If the value of this field is zero, then all actor signals will be output ones; some or all of those signals can be input ones. If the value of this field is non-zero, then the field must contain a pointer to an array that holds nsig (see the next field) elements. A zero element indicates that a signal with a corresponding identifier is an input signal. A non-zero element indicates that a signal with a corresponding identifier is an output signal and possibly input one. The array must contain at least two non-zero elements. When creating an actor, the array is copied to an internal structure of the actor.

— Field: int nsig

The total number of signals of the actor. Must be greater than 1 and less than or equal to QSMM_SIG_MAX+1.

— Structure: qsmm_pair_sig_s

This structure represents a pair of signals, e.g. a signal range. It contains the following fields.

— Field: qsmm_sig_t first

The first element of the pair. If the pair represents a range of signals, then the minimum value of signal identifier.

— Field: qsmm_sig_t second

The second element of the pair. If the pair represents a range of signals, then the maximum value of signal identifier.

— Structure: qsmm_actor_large_desc_s

This structure specifies parameters of large actor. It contains only one field, but is designed as a structure to simplify adding new parameters of large actor in future versions of the package.

— Field: int tree_arity

The maximum number of child nodes of every node of Huffman trees used for stochastic selection of optimal output signals by the large actor. Must be greater than 1. You can use value 2 in most cases. It is better to use the number of output signals of large actor equal to positive integer powers of the value of this field.

To improve compatibility with future versions of the library, an instance of structure qsmm_actor_large_desc_s (a pointer to which is assigned to field large_desc_p of structure qsmm_actor_desc_s) should be zeroed using function memset before setting the value of field tree_arity.

Below there is given sample code for creating a small actor.

     int rc;
     qsmm_actor_t actor=0;
     struct qsmm_actor_desc_s actor_desc;
     struct qsmm_actor_sig_spec_in_out_s *spec_in_out_p;
     memset(&actor_desc,0,sizeof(actor_desc));
     actor_desc.nspur=1;
     actor_desc.ngram_sz=3;
     actor_desc.compat=1;
     actor_desc.sig_spec_type=QSMM_ACTOR_SIG_SPEC_IN_OUT;
     spec_in_out_p=&actor_desc.sig_spec.in_out;
     spec_in_out_p->nsig_in=6;
     spec_in_out_p->nsig_out=4;
     if ((rc=qsmm_actor_create(&actor_desc,&actor))<0)
         fprintf(stderr,"qsmm_actor_create: %s\n",qsmm_err_str(rc));

Below there is given sample code for creating a large actor.

     int rc;
     qsmm_actor_t actor=0;
     struct qsmm_pair_sig_s range_sig[3];
     struct qsmm_actor_desc_s actor_desc;
     struct qsmm_actor_large_desc_s large_desc;
     struct qsmm_actor_sig_spec_in_out_s *spec_in_out_p;
     memset(&range_sig,0,sizeof(range_sig));
     range_sig[0].second=5;
     range_sig[1].second=5;
     range_sig[2].second=5;
     memset(&large_desc,0,sizeof(large_desc));
     large_desc.tree_arity=2;
     memset(&actor_desc,0,sizeof(actor_desc));
     actor_desc.nspur=1;
     actor_desc.ngram_sz=3;
     actor_desc.compat=1;
     actor_desc.sparse_fill_max=0.2;
     actor_desc.sig_spec_type=QSMM_ACTOR_SIG_SPEC_IN_OUT;
     spec_in_out_p=&actor_desc.sig_spec.in_out;
     spec_in_out_p->nsig_in=6;
     spec_in_out_p->nsig_out=4096;
     actor_desc.range_sig_p=range_sig;
         // signal list, which defines an action choice state, is assumed to
         // consist only of input signals of the actor
     actor_desc.large_desc_p=&large_desc;
     if ((rc=qsmm_actor_create(&actor_desc,&actor))<0)
         fprintf(stderr,"qsmm_actor_create: %s\n",qsmm_err_str(rc));

Parameters specified when creating an actor can be obtained later using the following functions.

— Function: int qsmm_get_actor_nspur (qsmm_actor_t actor)

This function returns a positive integer equal to the number of spur types used by actor, which is specified in field nspur of structure qsmm_actor_desc_s when creating the actor by function qsmm_actor_create.

— Function: int qsmm_get_actor_ngram_sz (qsmm_actor_t actor)

This function returns a positive integer number equal to the length of a list of signal identifiers, which defines an action choice state of an actor. It is the length, which is specified in field ngram_sz of structure qsmm_actor_desc_s when creating the actor using function qsmm_actor_create.

— Function: int qsmm_get_actor_profile_pool_sz (qsmm_actor_t actor)

This function returns a non-negative integer number equal to the size of the pool of probabilities lists in normal form of actor. It is the size, which is specified in field profile_pool_sz of structure qsmm_actor_desc_s when creating the actor using function qsmm_actor_create.

— Function: int qsmm_get_actor_compat (qsmm_actor_t actor)

This function returns a non-negative integer number equal to a compatibility level of algorithms used by actor, which is specified in field compat of structure qsmm_actor_desc_s when creating the actor by function qsmm_actor_create.

— Function: int qsmm_get_actor_nsig (qsmm_actor_t actor)

This function returns a positive integer equal to the total number of signals of actor.

If the value of field sig_spec_type of structure qsmm_actor_desc_s passed to function qsmm_actor_create when creating the actor is equal to QSMM_ACTOR_SIG_SPEC_IN_OUT, then the total number of signals will be the sum of values of fields nsig_in and nsig_out of structure qsmm_actor_sig_spec_in_out_s specified in field sig_spec.in_out of structure qsmm_actor_desc_s.

If the value of field sig_spec_type is equal to QSMM_ACTOR_SIG_SPEC_MASK, then the total number of signals will be the value of field sig_spec.mask.nsig of structure qsmm_actor_desc_s.

— Function: int qsmm_get_actor_nsig_out (qsmm_actor_t actor)

This function returns a positive integer equal to the number of output signals of actor.

If the value of field sig_spec_type of structure qsmm_actor_desc_s passed to function qsmm_actor_create when creating the actor is equal to QSMM_ACTOR_SIG_SPEC_IN_OUT, then the number of output signals will be the value of field sig_spec.in_out.nsig_out of structure qsmm_actor_desc_s.

If the value of field sig_spec_type is equal to QSMM_ACTOR_SIG_SPEC_MASK, then the number of output signals will be the number of non-zero elements in an array pointed by field sig_spec.mask.is_sig_ctrl_p of structure qsmm_actor_desc_s.

— Function: double qsmm_get_actor_sparse_fill_max (qsmm_actor_t actor)

This function returns the maximum fill ratio for vectors that hold relative probabilities of output signals choice, on the basis of which actor decides when to use sparse vectors instead of ordinary vectors. It is a value, which is specified in field sparse_fill_max of structure qsmm_actor_desc_s when creating the actor using function qsmm_actor_create.

— Function: const struct qsmm_pair_sig_s * qsmm_get_actor_range_sig (qsmm_actor_t actor)

This function returns a pointer to an array (stored in an internal structure of an actor), which describes possible ranges of signals in a list of signal identifiers, which defines an action choice state. The contents of the array are specified using field range_sig_p of structure qsmm_actor_desc_s when creating the actor by function qsmm_actor_create. The number of elements in the array is equal to the length of a list of signal identifiers, which defines an action choice state. That length can be retrieved by function qsmm_get_actor_ngram_sz.

A datatype, which represents a statistics storage handle, is qsmm_storage_t. The handle of statistics storage of an actor can be obtained using the following function.

— Function: qsmm_storage_t qsmm_get_actor_storage (qsmm_actor_t actor)

This function returns the handle of storage used by actor for collecting statistics on the event history. If the actor is the large one, then the storage will hold only part of the statistics. Other part of the statistics will be stored in Huffman trees created within a multinode model that corresponds to the large actor. This function never returns 0.

A datatype, which represents a multinode model that corresponds to a large actor, is qsmm_t. The handle of a multinode model of an actor can be obtained using the following function.

— Function: qsmm_t qsmm_get_actor_large_model (qsmm_actor_t actor)

This function returns the handle of a multinode model used by a large actor to stochastically generate optimal output signals. If an actor is not large one, then 0 will be returned. This function can be used to determine whether an actor is large one, in which case the function always returns a non-zero value.


Next: , Previous: Creating an Actor, Up: Optimal Action Generation Engine

2.5 Incrementing Time

There are two types of time associated with an actor.

The actor tracks discrete time equal to the number of input signals received plus the number of output signals emitted by the actor since the beginning of its operation. The actor normally uses discrete time when calculating probabilities of output signals choice.

The actor can also track continuous time equal to a logical period of time the event history occupies since the start of actor operation. The actor can use continuous time to compute spur increment velocities when calculating probabilities of output signals choice.

To convey information to an actor that a period of continuous time has passed in event history, during which there were no input signals received by the actor, the following function can be used.

— Function: int qsmm_actor_time_delta (qsmm_actor_t actor, double time_delta)

This function increments by time_delta the value of continuous time associated with actor.

On success, a non-negative value is returned. If time_delta is not a finite number or if the incremented value of continuous time becomes non-finite or negative, then no increment will be performed, and negative error code QSMM_ERR_INVAL will be returned.

To get the current value of continuous time associated with an actor, the following function can be used.

— Function: double qsmm_get_actor_continuous_time (qsmm_actor_t actor)

This function returns the value of continuous time associated with actor. The returned value is always finite and non-negative.

To get or set the value of discrete time associated with an actor, the following functions can be used.

— Function: long qsmm_get_actor_discrete_time (qsmm_actor_t actor)

This function returns the value of discrete time associated with actor.

— Function: void qsmm_set_actor_discrete_time (qsmm_actor_t actor, long tmd)

This function sets the value of discrete time associated with actor to tmd.

A type of time used by a small actor when calculating spur increment velocity is specified via the following enumeration.

— Enumeration: qsmm_time_e

This enumeration specifies a type of time. It contains the following elements.

QSMM_TIME_DISCRETE
Discrete time.
QSMM_TIME_CONTINUOUS
Continuous time.

A large actor normally uses continuous time when calculating increment velocities of spur of types, the number of which was specified when creating the actor. Internally, a small actor associated with the large actor normally uses discrete time when calculating the increment velocity of the automatic spur.

To retrieve or set the type of time, according to which a small actor calculates increment velocity of spur of a particular type, the following functions can be used.

— Function: int qsmm_get_actor_spur_time (qsmm_actor_t actor, int spur_type, enum qsmm_time_e *time_type_p)

This function sets *time_type_p to a type of time, according to which actor calculates increment velocity for spur type spur_type. Spur types have zero-based indices. If time_type_p is 0, then *time_type_p will not be set.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of spur_type is negative or is greater than or equal to the number of spur types specified when creating the actor.
QSMM_ERR_NOSYS
The actor is the large one.

— Function: int qsmm_set_actor_spur_time (qsmm_actor_t actor, int spur_type, enum qsmm_time_e time_type)

This function sets to time_type a type of time, according to which actor calculates increment velocity for spur type spur_type. Spur types have zero-based indices. If the value of time_type is invalid, then behavior of the actor will be undefined.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of spur_type is negative or is greater than or equal to the number of spur types specified when creating the actor.
QSMM_ERR_NOSYS
The actor is the large one.

Function qsmm_actor_create initializes types of time for all spur types of a newly created small actor to QSMM_TIME_CONTINUOUS.


Next: , Previous: Incrementing Time, Up: Optimal Action Generation Engine

2.6 Incrementing Spur

The value of the spur of given type an actor keeps track of can be incremented using the following function.

— Function: int qsmm_actor_spur_delta (qsmm_actor_t actor, int spur_type, double spur_delta)

This function increments by spur_delta the value of the spur of type spur_type an actor has been accumulating. Spur types, the number of which was specified when creating the actor, have zero-based indices. Special spur type -1 of a large actor corresponds to the automatic spur of a small actor associated with the large actor. The value of spur_delta can be negative.

On success, a non-negative value is returned. If the actor is the small one and spur_type is negative, or if the actor is the large one and spur_type is less than -1, or if spur_type is greater than or equal to the number of spur types specified when creating the actor, or if spur_delta is non-finite, or if the incremented value of the spur becomes non-finite, then no increment will be performed and negative error code QSMM_ERR_INVAL will be returned.

To get the current value of the spur of given type an actor keeps track of, the following function can be used.

— Function: int qsmm_get_actor_spur (qsmm_actor_t actor, int spur_type, double *spur_p)

This function sets *spur_p to a current value of the spur of type spur_type an actor has been accumulating. Spur types, the number of which was specified when creating the actor, have zero-based indices. Special spur type -1 of a large actor corresponds to the automatic spur of a small actor associated with the large actor. If spur_p is 0, then *spur_p will not be set, otherwise *spur_p will always be finite.

On success, a non-negative value is returned. If the actor is the small one and spur_type is negative, or if the actor is the large one and spur_type is less than -1, or if spur_type is greater than or equal to the number of spur types specified when creating the actor, then negative error code QSMM_ERR_INVAL will be returned.


Next: , Previous: Incrementing Spur, Up: Optimal Action Generation Engine

2.7 Event History N-gram

An action choice state is a list of signal identifiers of fixed length, on the basis of which an actor can generate an output signal. That length has to be specified in field ngram_sz of structure qsmm_actor_desc_s when creating the actor using function qsmm_actor_create and can be retrieved later by function qsmm_get_actor_ngram_sz.

There is a window associated with an actor, the contents of which are n-grams of length ngram_sz from the event history. When creating an actor, elements of the window are initialized to 0. For example, when ngram_sz is equal to 3, the contents of the window just after creating the actor are <0, 0, 0>.

The contents of the window shift one signal left and a new signal from the event history becomes the last signal of the window after a call to function qsmm_actor_shl_sig, qsmm_actor_reg_sig_in, or qsmm_actor_reg_sig_action. Shifting the contents of the window one signal left also implies incrementing by 1 discrete time tracked by the actor.

To shift the contents of the window one signal left and append a new signal from the event history, so that the window would contain an n-gram that represents an action choice state, function qsmm_actor_reg_sig_in should be used. This function updates statistics, collected over the event history, using information recorded when the same action choice state has occurred the previous time in the event history.

To shift the contents of the window one signal left and append a new signal, which is an output signal generated by the actor (or another output signal the application program has chosen to emit), function qsmm_actor_reg_sig_action should be used. You should usually call this function after a call to qsmm_actor_reg_sig_in. It records for an action choice state discrete and continuous time of the last occurrence of the action choice state in the event history, an output signal emitted in that action choice state, and current value of the spur of every type the actor has been accumulating.

Note: because function qsmm_actor_reg_sig_action records time and values of spur for an action choice state, which has just occurred in the event history, functions qsmm_actor_time_delta and qsmm_actor_spur_delta usually should not be called between a call to qsmm_actor_reg_sig_in and a subsequent call to qsmm_actor_reg_sig_action.

In all other cases, to shift the contents of the window one signal left and append a new signal from the event history, function qsmm_actor_shl_sig should be used. This function does not update statistics collected over the event history.

In ngram_chg, there is shown an example of changing a window, which holds current n-gram from the event history, caused by calls to Actor API functions. This example corresponds to first eight events from event history shown in Figure 2.1. N-grams, which represent action choice states, are marked by thick lines.

Example of changing current n-gram of event history

Figure 2.4: example of changing current n-gram of event history

Functions, which shift the contents of the window one signal left and increment by 1 discrete time tracked by an actor, are described in detail below.

— Function: int qsmm_actor_shl_sig (qsmm_actor_t actor, qsmm_sig_t sig_last, qsmm_sig_t *sig_first_p)

This function shifts one signal left the contents of a window of actor, which holds current n-gram from the event history, and appends signal sig_last to the end of the window. Discrete time tracked by the actor is incremented by 1. A signal, which was at the beginning of the window before the shift, is returned in *sig_first_p if sig_first_p is not 0.

For example, if the window contained signals <3, 6, 2>, then after executing lines of code

          qsmm_sig_t sig_first=QSMM_SIG_INVALID;
          qsmm_actor_shl_sig(actor,4,&sig_first);

the window would contain signals <6, 2, 4>, and sig_first would be equal to 3.

On success, the function returns a non-negative value. If sig_last is greater than or equal to the number of signals of the actor, then negative error code QSMM_ERR_INVAL will be returned.

— Function: int qsmm_actor_reg_sig_in (qsmm_actor_t actor, qsmm_sig_t sig)

This function shifts one signal left the contents of a window of actor, which holds current n-gram from the event history, and appends signal sig to the end of the window. Discrete time tracked by the actor is incremented by 1. The resulting contents of the window are considered to be an n-gram that represents an action choice state. If that action choice state did not occur earlier in the event history, then the function finishes execution.

If that action choice state did occur earlier in the event history, then for a pair, which consists of the action choice state and an output signal emitted when the action choice state has occurred the previous time in the event history (that moment is referred to below as the time of the last occurrence of the pair), statistics is updated in the following way:

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of sig is greater than or equal to the number of signals of the actor.
QSMM_ERR_NGRAM
After shifting one signal left the contents of an actor window, which holds current n-gram from the event history, and appending signal sig to the end of the window, that window would correspond to an invalid action choice state. To determine the validity of an action choice state, it is checked for accordance with allowed ranges of signal identifiers specified using field range_sig_p of structure qsmm_actor_desc_s when creating the actor.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the actor in indeterminate state.
QSMM_ERR_NOMEM
A statistics storage access function did return out of memory error. This could leave the actor in indeterminate state.

When you want to store an action choice state n-gram of length greater than 1 in an actor window that holds current n-gram from the event history, you normally use a series of calls to qsmm_actor_shl_sig followed by a call to qsmm_actor_reg_sig_in:

     int ii;
     for (ii=0; ii<NGRAM_SZ-1; ii++)
         if (qsmm_actor_shl_sig(actor,sig_ngram[ii],0)<0) goto Error;
     if (qsmm_actor_reg_sig_in(actor,sig_ngram[NGRAM_SZ-1])<0) goto Error;

Note that, in some cases, it is necessary not to completely replace the contents of the window, so NGRAM_SZ should be changed to a value less than the length of a list of signal identifiers, which represents an action choice state.

— Function: int qsmm_actor_reg_sig_action (qsmm_actor_t actor, qsmm_sig_t sig)

This function registers signal sig as an output signal of actor emitted in an action choice state, which corresponds to the contents of a window that holds current n-gram from the event history.

The function records the following information for that action choice state:

Finally, the function shifts one signal left the contents of the window, which holds current n-gram from the event history, and appends signal sig to the end of the window. Discrete time tracked by the actor is incremented by 1.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Signal sig is not an output signal of the actor.
QSMM_ERR_NGRAM
The contents of an actor window, which holds current n-gram from the event history, correspond to an invalid action choice state. To determine the validity of an action choice state, it is checked for accordance with allowed ranges of signal identifiers specified using field range_sig_p of structure qsmm_actor_desc_s when creating the actor.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the actor's memory in indeterminate state. If the actor's memory is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the actor's memory state will become determinate.
QSMM_ERR_NOMEM
A statistics storage access function did return out of memory error. This could leave the actor's memory in indeterminate state. If the actor's memory is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the actor's memory state will become determinate.

To shift the contents of the window one signal right and decrement by 1 discrete time tracked by the actor, the following function can be used.

— Function: int qsmm_actor_shr_sig (qsmm_actor_t actor, qsmm_sig_t sig_first, qsmm_sig_t *sig_last_p)

This function shifts one signal right the contents of an actor window, which holds current n-gram from the event history, and prepends signal sig_first to the beginning of the window. Discrete time tracked by the actor is decremented by 1. A signal, which was at the end of the window before the shift, is returned in *sig_last_p if sig_last_p is not 0.

On success, the function returns a non-negative value. If sig_first is greater than or equal to the number of signals of the actor, then negative error code QSMM_ERR_INVAL will be returned.

When using function qsmm_actor_shr_sig, remember that functions qsmm_actor_reg_sig_in and qsmm_actor_reg_sig_action should normally be called for increasing (or at least for equal) values of discrete time tracked by the actor. That is, after calling function qsmm_actor_shr_sig certain number of times, function qsmm_actor_shl_sig usually should be called the corresponding number of times to restore discrete time to the original value.

In some cases, it is necessary to analyze actor's behavior when current action choice state is another state. To do that, it is required to save the contents of the window, which holds current n-gram from the event history, replace the contents with another n-gram, call an Actor API function that calculates probabilities of emitting output signals, and finally restore the original contents of the window. An internal buffer, which holds the contents of the window, can be obtained using the following function.

— Function: qsmm_sig_t * qsmm_get_actor_sig_ngram (qsmm_actor_t actor)

This function returns a pointer to an internal buffer of actor, which represents a window that holds current n-gram from the event history. This function never returns 0. The number of elements in the buffer is equal to the value specified in field ngram_sz of structure qsmm_actor_desc_s when creating the actor using function qsmm_actor_create, which is the value returned by function qsmm_get_actor_ngram_sz.

To enumerate n-grams of action choice states, information on which is stored in the actor's memory, the following function can be used.

— Function: int qsmm_actor_enum_ngrams (qsmm_actor_t actor, int ngram_prefix_sz, const qsmm_sig_t *sig_ngram_prefix_p, qsmm_enum_ngrams_callback_func_t callback_func, void *paramp)

This function enumerates n-grams of action choice states, information on which is stored in the memory of actor. For a small actor, these are states, information on which is held in statistics storage of the actor, a handle to which can be obtained using function qsmm_get_actor_storage. For a large actor, these are states from the union of the following state sets:

The function enumerates the above action choice states that have prefix sig_ngram_prefix_p of length ngram_prefix_sz. If ngram_prefix_sz is 0, then sig_ngram_prefix_p can be 0.

The process of enumeration is a repeated calling callback function callback_func, to which an action choice state n-gram and user parameter paramp are passed. If the callback function returns a positive value, then the process of enumeration will be continued. If the callback function returns zero, then the process of enumeration will be terminated, and function qsmm_actor_enum_ngrams will report success. If the callback function returns a negative value, then the process of enumeration will be terminated, and function qsmm_actor_enum_ngrams will report failure.

In the current implementation, function qsmm_actor_enum_ngrams does not support recursive calling from the callback function for the same actor.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of ngram_prefix_sz is less than 0 or is greater than the length of a list of signal identifiers, which defines an action choice state.
QSMM_ERR_CALLBACK
The callback function did return an error.
QSMM_ERR_STORAGE
Statistics storage failure.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

The type of a pointer to a callback function, which is called for every enumerated action choice state, is described below.

— Data type: qsmm_enum_ngrams_callback_func_t

This is a type of callback function pointer, to which the following declaration corresponds:

          typedef int (*qsmm_enum_ngrams_callback_func_t)(
              qsmm_actor_t actor,
              const qsmm_sig_t *sig_ngram_p,
              void *paramp);

The callback function is called for every enumerated action choice state of an actor. The list of signal identifiers of an action choice state is passed via argument sig_ngram_p. The length of that list is equal to a value, which has to be specified in field ngram_sz of structure qsmm_actor_desc_s when creating the actor using function qsmm_actor_create and which can be retrieved later by function qsmm_get_actor_ngram_sz. A user parameter is passed via argument paramp.

The callback function might return a positive value if the process of enumeration should be continued, zero if the process of enumeration should be terminated, or a negative value on error.

To remove information on an action choice state from the actor's memory, the following function can be used.

— Function: int qsmm_actor_remove_ngram (qsmm_actor_t actor, const qsmm_sig_t *sig_ngram_p)

This function removes information on an action choice state, specified by list of signal identifiers sig_ngram_p, from the memory of actor. The length of the list is equal to a value, which has to be specified in field ngram_sz of structure qsmm_actor_desc_s when creating the actor using function qsmm_actor_create and which can be retrieved later by function qsmm_get_actor_ngram_sz.

For a small actor, the function removes information on the action choice state held in statistics storage of the actor, a handle to which can be obtained using function qsmm_get_actor_storage. For a large actor, the function removes the following pieces of information if they exist in the actor's memory:

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NGRAM
Argument sig_ngram_p specifies an invalid action choice state n-gram. To determine the validity of an action choice state n-gram, it is checked for accordance with allowed ranges of signal identifiers specified using field range_sig_p of structure qsmm_actor_desc_s when creating the actor.
QSMM_ERR_NOTFOUND
The actor's memory does not contain information on the action choice state, nothing to remove.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the actor's memory in indeterminate state. If the actor's memory is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds or returns QSMM_ERR_NOTFOUND, then the actor's memory state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the actor's memory in indeterminate state. If the actor's memory is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds or returns QSMM_ERR_NOTFOUND, then the actor's memory state will become determinate.


Next: , Previous: Event History N-gram, Up: Optimal Action Generation Engine

2.8 Generating an Optimal Action

To calculate probabilities of emitting output signals for an action choice state, which is stored in an actor window that holds current n-gram from the event history, the following function can be used.

— Function: int qsmm_actor_calc_action_prob (qsmm_actor_t actor, int rez1, qsmm_sig_t sig_beg, qsmm_sig_t sig_end, enum qsmm_prob_e prob_type)

This function fills the internal array of actor with probabilities of type prob_type calculated for an action choice state, which is stored in an actor window that holds current n-gram from the event history. Argument rez1 is reserved for future use and must be equal to 0.

For a small actor, the contents of the internal array that holds probabilities calculated can be used to stochastically choose an optimal output signal by function qsmm_get_actor_sig_action. For a large actor, function qsmm_actor_calc_action_prob operates much more slowly and should be used only when values of probabilities have to be obtained. Large actors support calling function qsmm_get_actor_sig_action, which returns control quickly, without prior calling function qsmm_actor_calc_action_prob.

Arguments sig_beg and sig_end can be both zero or may specify identifiers of the first signal (inclusive) and of the last signal (exclusive) of a signal segment for which the function should calculate probabilities. In the internal array that holds probabilities calculated, the probability of the first signal of that signal segment has offset sig_beg. Array elements within the signal segment, which do not correspond to output signals, are set to 0. If sig_end is 0, then the total number of actor signals will be used for the value of the last signal of the signal segment. The function will execute faster when the signal segment is shorter, so it is recommended to specify the segment as precisely as possible.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of sig_beg is greater than or equal to a value (incremented by 1) of the last signal of the signal segment, or the value of sig_end is greater than the total number of signals of the actor, or the value of prob_type is invalid.
QSMM_ERR_NGRAM
The contents of an actor window, which holds current n-gram from the event history, correspond to an invalid action choice state. To determine the validity of an action choice state, it is checked for accordance with allowed ranges of signal identifiers specified using field range_sig_p of structure qsmm_actor_desc_s when creating the actor.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave a large actor in indeterminate state. If the large actor is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the actor's state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave a large actor in indeterminate state. If the large actor is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the actor's state will become determinate.

A type of probabilities function qsmm_actor_calc_action_prob must calculate is specified using an enumeration described below. The enumeration is also used in several other cases.

— Enumeration: qsmm_prob_e

This enumeration specifies a type of probabilities. It contains the following elements.

QSMM_PROB_AGGR
Aggregate probabilities based on learned and profile probabilities.

For a small actor, to calculate probabilities of type QSMM_PROB_AGGR, probabilities of types QSMM_PROB_LEARNT and QSMM_PROB_PROFILE (see below) are calculated, each learned probability is multiplied by a corresponding profile probability, and the resulting array of values is normalized. For a large actor, the above operation is performed by the small actor for all nodes of the Huffman tree.

For a small actor, if you do not use profile probabilities, then it will be faster to calculate probabilities of type QSMM_PROB_LEARNT, which are equal to probabilities of type QSMM_PROB_AGGR in this case.

For a large actor, probabilities of type QSMM_PROB_AGGR are ordinarily equal to probabilities of type QSMM_PROB_LEARNT, because profile probabilities typically follow only from a positional relationship of nodes of the Huffman tree (probabilities assigned to edges, which come from tree nodes to their child nodes, are usually equal), and those profile probabilities are taken into account when calculating probabilities of type QSMM_PROB_LEARNT.

QSMM_PROB_LEARNT
Learned probabilities, according to which optimal actions may be produced.
QSMM_PROB_PROFILE
For a small actor, profile probabilities specified a priori in statistics storage (see Structures for Accessing Storage, for information on how to pass profile probabilities to storage access functions to write them to storage). For a large actor, profile probabilities, which follow from the structure of a Huffman tree that corresponds to an action choice state.
QSMM_PROB_FQ
Probabilities proportional to observed frequencies of emitting output signals. Those frequencies are automatically incremented for output signals with identifiers passed to function qsmm_actor_reg_sig_action, but the increment is actually performed by function qsmm_actor_reg_sig_in when the same action choice state occurs again in the event history.
QSMM_PROB_COUNT
The last element of the enumeration equal to the number of supported types of probabilities.

To obtain a pointer to an internal array that holds probabilities of emitting output signals, which are calculated by function qsmm_actor_calc_action_prob, the following function can be used.

— Function: double * qsmm_get_actor_choice_sig_prob (qsmm_actor_t actor)

This function returns a pointer to an internal array of actor, which holds (possibly relative) probabilities of emitting output signals. Using that pointer, the array can be examined or modified by an application program. This function never returns 0. The number of elements in the array is equal to the total number of signals of the actor returned by function qsmm_get_actor_nsig. The array can be filled with probabilities of desired type using function qsmm_actor_calc_action_prob. For a small actor, the contents of the array are used by function qsmm_get_actor_sig_action to stochastically generate an output signal, in which case the array may contain values of positive infinity or relative probabilities that do not sum up to 1.

Function qsmm_get_actor_choice_sig_prob locks the internal array of probabilities: when the internal array is represented by a sparse vector, the sparse vector is converted to an ordinary vector; after the conversion, the actor always uses ordinary vectors until the internal array of probabilities is released by function qsmm_actor_choice_sig_prob_release. Forgetting to call function qsmm_actor_choice_sig_prob_release after a call to qsmm_get_actor_choice_sig_prob may dramatically decrease actor performance.

To release a locked internal array of probabilities, the following function can be used.

— Function: void qsmm_actor_choice_sig_prob_release (qsmm_actor_t actor)

This function releases an internal array of actor that holds (possibly relative) probabilities of emitting output signals, which could have been locked by function qsmm_get_actor_choice_sig_prob. If the array is not locked, then the function will do nothing. After the release, a pointer that might have been previously obtained by function qsmm_get_actor_choice_sig_prob must not be used.

When the internal array is locked or if is called for a large actor, function qsmm_actor_calc_action_prob uses an ordinary vector for storing probabilities of emitting output signals, so elements of that ordinary vector can be accessed via a pointer returned by function qsmm_get_actor_choice_sig_prob. When the internal array is released, function qsmm_actor_calc_action_prob called for a small actor can use an ordinary or sparse vector for storing probabilities of emitting output signals depending on properties of the probability profile and on the value of field sparse_fill_max of structure qsmm_actor_desc_s specified when creating the actor.

There is a way to obtain the handle of an ordinary or sparse vector that holds probabilities of emitting output signals. It is a cheap operation that does not perform locking or converting a sparse vector to an ordinary vector. The vector is returned as is, in read-only mode. See Ordinary and Sparse Vectors, for information on how to access the elements of the returned vector.

— Function: qsmm_vec_t qsmm_get_actor_choice_sig_prob_vec (qsmm_actor_t actor)

This function returns the handle of an ordinary or sparse vector of actor, which holds (possibly relative) probabilities of emitting output signals. This function never returns 0. The vector can be filled with probabilities of desired type using function qsmm_actor_calc_action_prob. For a small actor, the contents of the vector are used by function qsmm_get_actor_sig_action to stochastically generate an output signal.

To stochastically generate an output signal, the following function can be used.

— Function: int qsmm_get_actor_sig_action (qsmm_actor_t actor, int rez1, qsmm_sig_t sig_beg, qsmm_sig_t sig_end, qsmm_sig_t *sig_action_p)

If an actor is a small one, this function stochastically generates an output signal according to (possibly relative) probabilities stored in the internal array of the actor. That array can be filled with probabilities of desired type using function qsmm_actor_calc_action_prob. A pointer to the array is returned by function qsmm_get_actor_choice_sig_prob. A read-only view of the array, which can speed up accessing its contents, is returned by function qsmm_get_actor_choice_sig_prob_vec.

If an actor is a large one, this function stochastically generates an optimal output signal without using the aforementioned internal array. A Huffman tree is used to provide fast choice of an output signal (the number of those signals can be very large) by the actor according to probabilities of type QSMM_PROB_AGGR, which can be computed by function qsmm_actor_calc_action_prob. However, for large actors, there is no need to call that function before calling function qsmm_get_actor_sig_action; the number of calls to function qsmm_actor_calc_action_prob should be made as little as possible, because for those actors that function operates very slowly.

A generated output signal is stored in *sig_action_p if sig_action_p is not 0. Argument rez1 is reserved for future use and must be equal to 0.

Arguments sig_beg and sig_end can be both zero or may specify identifiers of the first signal (inclusive) and of the last signal (exclusive) of a signal segment, to which the generated output signal should belong. However, for a large actor, the signal segment must include all output signals of the actor; see Specifying Weights of Output Signals, for information on how to choose an optimal output signal from different subsets of output signals of a large actor. In the internal array of small actor, a probability of the first signal of that signal segment has offset sig_beg. If sig_end is 0, then the total number of actor signals will be used for the value of the last signal of the signal segment. The function will execute faster when the signal segment is shorter, so it is recommended to specify the segment as precisely as possible. For a small actor, the function does not check whether a signal it generates actually belongs to a set of output signals of the actor: if an element of the internal array within the signal segment has a positive value, then a corresponding signal can be potentially returned by the function. If one or more relative probabilities in the internal array within the signal segment have values of positive infinity, then for a small actor the function will choose an output signal as a uniformly distributed random element from the set of signals to which such infinite relative probabilities correspond.

To stochastically generate an output signal, the actor uses either a random number generator supplied via field rng of structure qsmm_actor_desc_s when creating the actor by function qsmm_actor_create or an instance of default random number generator allocated automatically if 0 is specified in that field. The handle of a random number generator used by an actor can be obtained via function qsmm_get_actor_rng, e.g. to seed the generator.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
One of the following conditions is met:
  • the value of sig_beg is greater than or equal to a value (incremented by 1) of the last signal of the signal segment;
  • the value of sig_end is greater than the total number of signals of the actor;
  • the actor is the large one, and the value of sig_beg is greater than the lowest output signal identifier of the actor;
  • the actor is the large one, and the value of sig_end is less than the highest output signal identifier of the actor plus 1;
  • the actor is the small one and the internal array does not contain (within the signal segment) relative probabilities equal to positive infinity, but the sum of relative probabilities within the signal segment is not finite.

QSMM_ERR_WEIGHT
The actor is the small one, and a negative number or a non-finite number, which is not a positive infinity, was encountered in the internal array within the signal segment.
QSMM_ERR_NOCHOICE
The actor is the small one, and the internal array does not contain at least one positive element within the signal segment.
QSMM_ERR_NGRAM
The actor is the large one, and the contents of an actor window, which holds current n-gram from the event history, correspond to an invalid action choice state. To determine the validity of an action choice state, it is checked for accordance with allowed ranges of signal identifiers specified using field range_sig_p of structure qsmm_actor_desc_s when creating the actor.
QSMM_ERR_STORAGE
Failure of statistics storage of a large actor. This could leave the large actor in indeterminate state.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation by a large actor. This could leave the large actor in indeterminate state.

For a small actor, an optimal action is typically produced and registered using a sequence of function calls like this:

     qsmm_sig_t sig_action=QSMM_SIG_INVALID;
     if (qsmm_actor_calc_action_prob(actor, 0, NSIG_IN, NSIG_IN+NSIG_OUT,
                                     QSMM_PROB_LEARNT)<0)
         goto Error;
     if (qsmm_get_actor_sig_action(actor, 0, NSIG_IN, NSIG_IN+NSIG_OUT,
                                   &sig_action)<0)
         goto Error;
     if (qsmm_actor_reg_sig_action(actor,sig_action)<0) goto Error;

However, if, in a particular case, the application program chooses on its own which output signal to emit, then calls to qsmm_actor_calc_action_prob and qsmm_get_actor_sig_action can be omitted, but the small actor should be still notified of an output signal actually emitted using a call to qsmm_actor_reg_sig_action.

For a large actor, to produce and register an optimal action, a call to qsmm_actor_calc_action_prob is redundant and only decreases performance. The sequence of function calls looks like this:

     qsmm_sig_t sig_action=QSMM_SIG_INVALID;
     if (qsmm_get_actor_sig_action(actor, 0, NSIG_IN, NSIG_IN+NSIG_OUT,
                                   &sig_action)<0)
         goto Error;
     if (qsmm_actor_reg_sig_action(actor,sig_action)<0) goto Error;

Large actors do not support choosing an output signal by an application program. That is, for a large actor, it is incorrect to omit the call to qsmm_get_actor_sig_action before calling qsmm_actor_reg_sig_action, or to pass to the latter function a signal identifier other than that, which was returned by a preceding call to qsmm_get_actor_sig_action.

In QSMM version 1.16 there is introduced a function, which retrieves a probability of the last output signal generated. The function returns control quickly even when called for a large actor and can be used to calculate the time of emitting an output signal, e.g. according to the exponential probability distribution.

— Function: double qsmm_get_actor_prob_action (qsmm_actor_t actor)

This function returns the probability of the last output signal generated by actor in function qsmm_get_actor_sig_action. For a small actor, it is a probability that corresponds to the element of an internal array of the actor, which holds relative probabilities of emitting output signals. For a large actor, it is the probability of a path from the root of the Huffman tree to its leaf that corresponds to the output signal. The returned value is always in the range 0 to 1 (inclusive). If function qsmm_get_actor_sig_action is not yet called, then 0 will be returned.


Next: , Previous: Generating an Optimal Action, Up: Optimal Action Generation Engine

2.9 Customizing a Relative Probability Function

Efficient operation of a small or large actor, i.e. optimality of actions the actor produces, depends on the form of a function that returns a relative probability of output signal choice. It may well be true that, for different kinds of applications, different functions are more suitable. The QSMM package provides a few built-in functions, which the programmer may select for use by an actor. Unfortunately, the author of the package experiences problems in: (1) providing a sound theoretical basis for using those functions in the package (that basis might not exist for some functions); (2) devising functions that give better efficiency than the functions currently provided in the package. Author's own experimenting had shown that, for some special cases, there do exist other functions, which give better efficiency, but he did fail to find general forms for those functions, which would make possible to use them for wider ranges of values of actor parameters. To provide a way to solve the above problems by developers, in QSMM starting from version 1.15, there is supported a possibility to specify via the Actor API a custom relative probability function for use by an actor. To retrieve or set the type of relative probability function, the following functions can be used.

— Function: enum qsmm_relprob_e qsmm_get_actor_relprob_type (qsmm_actor_t actor)

This function returns the type of a function, which is used by an actor to calculate the relative probability of output signal choice. If the actor is the large one, then there will be returned the type of a function, which is used by a small actor associated with the large actor.

— Function: void qsmm_set_actor_relprob_type (qsmm_actor_t actor, enum qsmm_relprob_e relprob_type)

This function sets to relprob_type the type of a function, which is used by actor to calculate the relative probability of output signal choice. If the actor is the large one, then there will be set the type of a function, which is used by a small actor associated with the large actor. If the value of relprob_type is invalid, then behavior of the library will be undefined.

An enumeration that specifies supported types of relative probability function is described below.

— Enumeration: qsmm_relprob_e

This enumeration specifies supported types of a function, which is used by an actor to calculate the relative probability of output signal choice. It contains elements described below.

In the description of elements h is an action choice state, z is an output signal, ne is the number of spur types, T is actor temperature multiplied by some constant, F(h,z) is a relative probability of emitting an output signal, l is a mean discrete time period between an occurrence of action choice state h, in which output signal z was emitted, and subsequent occurrence of h, multiplied by a value returned by function qsmm_get_actor_naction_per_evt. That discrete time period is called a discrete cycle period for short. The mean number of output signals of the actor is denoted by nout (see Other Parameters of an Actor, for more information on computing that number). Spur weight for spur type i is denoted by W[i]. Function C(h,z,i), which is the ratio of spur increment velocities, is computed depending on the way of spur perception for spur type i (see further on in this section).

QSMM_RELPROB_BUILTIN1
The relative probability function is defined by formula
f_relprob_builtin1.png

where κ=2l. Paper “An Approach to Optimal Action Generation for a System that Interacts with the Environment” available from the project homepage provides more details pertaining to the formula.
QSMM_RELPROB_BUILTIN2
The relative probability function is defined by formula
f_relprob_builtin2.png

The author has empirically found the formula when developing version 1.15 of the package and making the actor generate output signals with frequencies proportional to probabilities of those output signals. The probabilities were specified a priori, and spur increments supplied to the actor were equal to logarithms of those probabilities.
QSMM_RELPROB_BUILTIN3
[New in QSMM 1.16] The relative probability function is defined by formula
f_relprob_builtin3.png

The formula is a simplification of a formula that corresponds to relative probability function type QSMM_RELPROB_BUILTIN2 and is meant for developers who prefer beauty and/or simplicity.
QSMM_RELPROB_USER1
The relative probability function is defined by formula
f_relprob_user1.png

where f(l) is a function of type qsmm_relprob_user1_func_t supplied by a programmer via a call to qsmm_set_actor_relprob_helper (see further on in this section). If function f(l) is not supplied, then 1 will be used for it.
QSMM_RELPROB_USER2
[New in QSMM 1.16] The relative probability function is defined by formula
f_relprob_user2.png

where g is a function of type qsmm_relprob_user2_func_t supplied by a programmer via a call to qsmm_set_actor_relprob_helper (see further on in this section). Function g receives the value of z and statistics associated with h and z that might be needed to devise a sophisticated relative probability function.

When creating a small actor, function qsmm_actor_create initializes the type of relative probability function to QSMM_RELPROB_BUILTIN1. When creating a large actor, function qsmm_actor_create initializes the type of relative probability function of a small actor, which is associated with the large actor, to QSMM_RELPROB_BUILTIN2.

Types for functions f(l) and g are described below.

— Data type: qsmm_relprob_user1_func_t

This is a type for a pointer to a helper function that can be supplied by a programmer when the type of relative probability function is set to QSMM_RELPROB_USER1. To this type the following declaration corresponds:

          typedef double (*qsmm_relprob_user1_func_t)(
              qsmm_actor_t actor,
              double cycle_period,
              void *paramp);

The handle of an actor that has called the helper function is passed via argument actor. The value of l is passed via argument cycle_period. A user parameter specified when setting the helper function is passed via argument paramp. The helper function should return the value of f(l).

— Data type: qsmm_relprob_user2_func_t

This is a type for a pointer to a helper function that should be supplied by a programmer when the type of relative probability function is set to QSMM_RELPROB_USER2. To this type the following declaration corresponds:

          typedef double (*qsmm_relprob_user2_func_t)(
              qsmm_actor_t actor,
              qsmm_sig_t sig_cycle,
              const struct qsmm_state_s *state_p,
              const struct qsmm_cycle_s *cycle_p,
              const struct qsmm_sspur_s *sspur_p,
              const struct qsmm_cspur_s *cspur_p,
              void *paramp);

The handle of an actor that has called the helper function is passed via argument actor. The value of z is passed via argument sig_cycle. Statistics associated with h and z is passed via arguments state_p, cycle_p, sspur_p, and cspur_p. See Structures for Accessing Storage, for a description of corresponding structures. A user parameter specified when setting the helper function is passed via argument paramp. The helper function should return the value of g.

To obtain working parameters of an actor, in the body of helper function such functions as qsmm_get_actor_nsig_ctrl and qsmm_get_actor_discrete_cycle_period_mean may be called. See Other Parameters of an Actor, for a detailed description of these functions.

To retrieve or set a helper function for an actor, the following functions can be used.

— Function: void qsmm_get_actor_relprob_helper (qsmm_actor_t actor, void **helper_func_pp, void **helper_func_param_pp)

This function retrieves information on a helper function, which is used by actor when calculating a relative probability of output signal choice. If helper_func_pp is not 0, then the function will set *helper_func_pp to a pointer to the helper function. If helper_func_param_pp is not 0, then the function will set *helper_func_param_pp to a user parameter of the helper function specified when setting the helper function for the actor. If there is no helper function set for the actor, and helper_func_pp is not 0, then this function will set *helper_func_pp to 0. If the actor is the large one, then this function will retrieve information on a helper function, which is used by a small actor associated with the large actor. A particular interpretation of a pointer, which is stored in *helper_func_pp, depends on the type of relative probability function set for the actor using a call to qsmm_set_actor_relprob_type.

— Function: void qsmm_set_actor_relprob_helper (qsmm_actor_t actor, void *helper_func_p, void *helper_func_param_p)

This function sets a pointer to a helper function, which is used by actor when calculating a relative probability of output signal choice, to helper_func_p and a user parameter of that helper function to helper_func_param_p. If the actor is the large one, then this function will set the helper function for a small actor associated with the large actor.

A particular interpretation of pointer helper_func_p depends on the type of relative probability function set for the actor using a call to qsmm_set_actor_relprob_type. Providing a pointer that does not conform with the type of relative probability function will lead to undefined behavior of the library. The value of helper_func_p can be 0, which means that there is no helper function set for the actor (this makes a certain sense when the type of relative probability function is QSMM_RELPROB_USER1).

To provide an example of using functions qsmm_set_actor_relprob_type and qsmm_set_actor_relprob_helper, let us suppose that a developer has proved that it is mathematically correct to use a function for calculating a relative probability of output signal choice, which differs from the function of type QSMM_RELPROB_BUILTIN2 in the following: instead of expression l+1, expression l is used in the numerator of the exponent. To make an actor to calculate a relative probability using this function, the developer may define a helper function like

     #include <math.h>
     
     static double get_relprob_exp_mlt(qsmm_actor_t actor,
                                       double cycle_period,
                                       void *paramp) {
         return log(qsmm_get_actor_nsig_ctrl(actor))*cycle_period/2;
     }

To set this helper function for use by an actor, the developer may write function calls

     qsmm_set_actor_relprob_type(actor,QSMM_RELPROB_USER1);
     qsmm_set_actor_relprob_helper(actor,&get_relprob_exp_mlt,0);

Alternatively, in QSMM version 1.16, to use that relative probability function, the developer may write function calls shown below. See further on in this section for a description of function qsmm_set_actor_ktemperature.

     qsmm_set_actor_relprob_type(actor,QSMM_RELPROB_BUILTIN3);
     qsmm_set_actor_ktemperature(actor,2);

In formulas for a relative probability function, which were given earlier in this section, function C(h,z,i), the ratio of spur increment velocities, is the function computed differently depending on the way of spur perception for spur type i. The way of spur perception determines whether that ratio will be generally positive or negative for generally positive or negative spur increments supplied to the actor. There are two supported ways of spur perception: normal and inverse. The normal way of spur perception provides generally positive ratio for generally positive spur increments and generally negative ratio for generally negative spur increments. The inverse way of spur perception provides generally negative ratio for generally positive spur increments and generally positive ratio for generally negative spur increments.

A way of spur perception for a spur type can be specified using the following enumeration.

— Enumeration: qsmm_spur_perception_e

This enumeration specifies a way of perception for a spur type. It contains elements described below.

In the description of elements t denotes discrete or continuous time used to compute spur increment velocity, E[i] denotes the value of the spur of type i the actor currently accumulated, H(h,z)[i] denotes the sum of increments of spur of type i for cycles of type <h,z> registered in the event history since the beginning of actor operation, and ω(h,z) denotes the sum of periods of discrete or continuous time for those cycles of type <h,z>. A cycle of type <h,z> is a segment of event history between occurrence of action choice state h, in which output signal z was emitted, and subsequent occurrence of h.

QSMM_SPUR_PERCEPTION_NORMAL
The value of C(h,z,i) is the ratio of spur increment velocity for a cycle type to the absolute value of the mean spur increment velocity:
f_percept_normal.png

When spur increments over the event history are non-negative, the value of C(h,z,i) increases as spur increment velocity increases. When spur increment velocity for a cycle type is positive and is equal to the mean spur increment velocity, the value of C(h,z,i) will be equal to 1.
QSMM_SPUR_PERCEPTION_INVERSE
The value of C(h,z,i) is the negative ratio of the absolute value of the mean spur increment velocity to spur increment velocity for a cycle type:
f_percept_inverse.png

When spur increments over the event history are negative, the value of C(h,z,i) increases as spur increment velocity increases. When spur increment velocity for a cycle type is negative and is equal to the mean spur increment velocity, the value of C(h,z,i) will be equal to 1.

An inverse way of spur perception could be used to countervail negative spur with another spur, which is negative, but which would be perceived as positive one because of an inverse way of perception specified.

To retrieve or set the way of perception for a spur type, the following functions can be used.

— Function: int qsmm_get_actor_spur_perception (qsmm_actor_t actor, int spur_type, enum qsmm_spur_perception_e *spur_perception_p)

This function sets *spur_perception_p to a way of perception for spur type spur_type of actor. Spur types, the number of which was specified when creating the actor, have zero-based indices. Special spur type -1 of a large actor corresponds to the automatic spur of a small actor associated with the large actor. If spur_perception_p is 0, then *spur_perception_p will not be set.

On success, the function returns a non-negative value. If the actor is the small one and spur_type is negative, or if the actor is the large one and spur_type is less than -1, or if spur_type is greater than or equal to the number of spur types specified when creating the actor, then negative error code QSMM_ERR_INVAL will be returned.

— Function: int qsmm_set_actor_spur_perception (qsmm_actor_t actor, int spur_type, enum qsmm_spur_perception_e spur_perception)

This function sets a way of perception for spur type spur_type of actor to spur_perception. Spur types, the number of which was specified when creating the actor, have zero-based indices. Special spur type -1 of a large actor corresponds to the automatic spur of a small actor associated with the large actor. If the value of spur_perception is invalid, then behavior of the actor will be undefined.

On success, the function returns a non-negative value. If the actor is the small one and spur_type is negative, or if the actor is the large one and spur_type is less than -1, or if spur_type is greater than or equal to the number of spur types specified when creating the actor, then negative error code QSMM_ERR_INVAL will be returned.

Function qsmm_actor_create initializes ways of perception for all spur types of a newly created actor to QSMM_SPUR_PERCEPTION_NORMAL.

To retrieve or set W[i], spur weight, the following functions can be used.

— Function: int qsmm_get_actor_spur_weight (qsmm_actor_t actor, int spur_type, double *weight_p)

This function sets *weight_p to weight for spur type spur_type of actor. Spur types, the number of which was specified when creating the actor, have zero-based indices. Special spur type -1 of a large actor corresponds to the automatic spur of a small actor associated with the large actor. If weight_p is 0, then *weight_p will not be set, otherwise *weight_p will always be finite.

On success, the function returns a non-negative value. If the actor is the small one and spur_type is negative, or if the actor is the large one and spur_type is less than -1, or if spur_type is greater than or equal to the number of spur types specified when creating the actor, then negative error code QSMM_ERR_INVAL will be returned.

— Function: int qsmm_set_actor_spur_weight (qsmm_actor_t actor, int spur_type, double weight)

This function sets weight for spur type spur_type of actor to weight. Spur types, the number of which was specified when creating the actor, have zero-based indices. Special spur type -1 of a large actor corresponds to the automatic spur of a small actor associated with the large actor.

On success, the function returns a non-negative value. If the actor is the small one and spur_type is negative, or if the actor is the large one and spur_type is less than -1, or if spur_type is greater than or equal to the number of spur types specified when creating the actor, or if weight is not finite, then negative error code QSMM_ERR_INVAL will be returned.

Function qsmm_actor_create initializes weights for all spur types of a newly created actor to 1.

In the formulas for a relative probability function, T denotes actor temperature multiplied by some constant. Actor temperature is a concept that follows from the use of an exponential form for this function, which in some ways resembles formulas related to the Boltzmann distribution. In most of the relative probability functions, actor temperature is a coefficient, by which all spur weights are divided.

The perfect relative probability function would be the one that provides maximum operating efficiency for an actor without the need to tune up the temperature of the actor by hand, i.e. the function for which default temperature equal to 1 could always be used. If the function is imperfect, its application is not intended one, or when applying the simulated annealing approach for solving a problem, to adjust the temperature of the actor to increase its operating efficiency, the following functions can be used.

— Function: double qsmm_get_actor_ktemperature (qsmm_actor_t actor)

This function returns the temperature (multiplied by some constant) of actor. The returned value is always finite and positive. If the actor is the large one, then the function will return the temperature of a small actor associated with the large actor.

— Function: int qsmm_set_actor_ktemperature (qsmm_actor_t actor, double val)

This function sets the temperature (multiplied by some constant) of actor to val. If the actor is the large one, then the function will set the temperature of a small actor associated with the large actor.

On success, the function returns a non-negative value. If val is not a finite number or is not a positive number, then negative error code QSMM_ERR_INVAL will be returned.

Function qsmm_actor_create initializes the temperature (multiplied by some constant) of a newly created actor to 1.


Next: , Previous: Customizing a Relative Probability Function, Up: Optimal Action Generation Engine

2.10 Specifying Weights of Output Signals

The weight of an output signal of actor is an additional coefficient, by which a relative probability of emitting the output signal is multiplied. To disable emitting an output signal, set its weight equal to 0.

It is essential to note that by reasons described in Other Parameters of an Actor, changing default weights of output signals of a small actor, in general, makes its behavior ill-defined and should be avoided. Changing the weights seems to be the correct procedure only for a large actor, because this procedure does not alter the number of choice alternatives at decision points along the pathways to output signals.

Every output signal of small actor can have weight assigned to it with or without binding to a specific action choice state. Output signals of large actor can only have weights bound to specific action choice states.

For a small actor, output signal weight bound to an action choice state can be specified using a preloaded probability profile for the action choice state or as a profile probability held in statistics storage of the small actor. See Structures for Accessing Storage, for information on how to pass profile probabilities to storage access functions to write them to storage. For a large actor, the only supported way to specify output signal weights is using preloaded probability profiles for action choice states.

If a small actor has a preloaded probability profile specified for current action choice state, then function qsmm_actor_calc_action_prob will copy output signal weights from the preloaded probability profile to a working array of output signal weights that ordinarily holds weights assigned to output signals without binding to a particular action choice state. The contents of that working array can be inspected or modified using functions qsmm_get_actor_sig_weight and qsmm_set_actor_sig_weight described below. Function qsmm_actor_create initializes weights of all output signals of a newly created small actor, which are stored in the working array, to 1. If an output signal of a small actor has a profile probability specified for an action choice state in statistics storage of the actor, then the overall weight of the output signal (which can be emitted in that action choice state) will be the product of a corresponding weight in the working array and that profile probability.

For a small actor, weights stored in the working array are always applied to relative probabilities computed within function qsmm_actor_calc_action_prob, disregarding of a requested type of probabilities to calculate. For a large actor, the working array is not used. After multiplying computed relative probabilities by corresponding weights, function qsmm_actor_calc_action_prob normalizes the resulting array to obtain probabilities that sum up to 1.

— Function: int qsmm_get_actor_sig_weight (qsmm_actor_t actor, qsmm_sig_t sig, double *weight_p)

This function sets *weight_p to the weight of output signal sig of actor. If weight_p is 0, then *weight_p will not be set, otherwise *weight_p will always be a finite and a non-negative number.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Signal sig is not an output signal of the actor.
QSMM_ERR_NOSYS
The actor is the large one.

— Function: int qsmm_set_actor_sig_weight (qsmm_actor_t actor, qsmm_sig_t sig, double weight)

This function sets the weight of output signal sig of actor to weight.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Signal sig is not an output signal of the actor, or weight is not a finite number or is a negative number.
QSMM_ERR_NOSYS
The actor is the large one.

A preloaded probability profile for an action choice state of an actor is defined by a list of probabilities in normal form and a one-to-one correspondence between elements of that list and identifiers of output signals of the actor. In QSMM, that correspondence is called a permutation of output signals for short. Splitting a preloaded probability profile into a probabilities list in normal form and a permutation of output signals is done to reduce the memory footprint of an actor, especially of large one.

For a small actor, the normal form of a list of probabilities is simply a sorted form of that list. For a large actor, the normal form of a given list of probabilities is a resulting list of probabilities that correspond to leaves of a Huffman tree created for a sorted form of the given list of probabilities. Indices of probabilities in the resulting list are equal to indices of corresponding source probabilities in the sorted list. Note that actually used profile probabilities, which follow from the structure of a Huffman tree, may differ from source profile probabilities for which that Huffman tree was created.

There is a concept of a pool of probabilities lists in normal form, which corresponds to an actor. Probabilities lists in normal form added to the pool cannot be removed from the pool, except by destroying the actor using function qsmm_actor_destroy. The size of the pool is specified in field profile_pool_sz of structure qsmm_actor_desc_s when creating the actor using function qsmm_actor_create and can be retrieved later by function qsmm_get_actor_profile_pool_sz.

The pool contains unique probabilities lists in normal form. That is, for a large actor, to elements of the pool there correspond Huffman trees with unique positional arrangements of nodes. For that type of actor, even when preloading a probability profile, to which there does correspond a probabilities list in normal form already contained in the pool, the actor creates a temporary Huffman tree to obtain the normal form of the list. The temporary Huffman tree takes up one element in the pool, which is why the size of the pool should generally be greater by 1 than the number of unique probabilities lists in normal form one needs to load into the pool.

There is also a concept of a pool of permutations of output signals, which corresponds to an actor. The pool contains unique permutations. As opposed to the pool of probabilities lists in normal form, the size of which has to be specified when creating the actor, the pool of permutations of output signals grows dynamically and its size does not need to be specified in advance. Permutations of output signals added to the pool cannot be removed from the pool, except by destroying the actor using function qsmm_actor_destroy.

To split a list of weights of output signals of an actor into a unique probabilities list in normal form and a unique permutation of output signals, the following function can be used.

— Function: int qsmm_actor_profile_add (qsmm_actor_t actor, qsmm_sig_t sig_beg, qsmm_sig_t sig_end, const double *weight_p, int *profile_p, int *permut_p)

This function preloads a list of weights of output signals into actor. The weights are passed via array weight_p. If weight_p is 0, then this will be interpreted as if array weight_p contains all weights equal to 1.

Arguments sig_beg and sig_end can be both zero or may specify identifiers of the first signal (inclusive) and the last signal (exclusive) for the list of weights. In array weight_p, the weight of the first signal of that list has offset sig_beg. If sig_end is 0, then the total number of actor signals will be used for the value of the last signal of the list. Values of elements of array weight_p, which do not correspond to output signals of the actor, are ignored.

If profile_p is not 0, then the function will set *profile_p to a non-negative index of a unique probabilities list in normal form that corresponds to the list of weights. That index will identify either a unique probabilities list in normal form just added to the pool of these lists or such a list which already contained in the pool. The length of the unique probabilities list in normal form is equal to the number of positive elements in array weight_p, which correspond to output signals of the actor within a range specified using sig_beg and sig_end.

If permut_p is not 0, then the function will set *permut_p to a non-negative index of a unique permutation of output signals, which corresponds to the list of weights. That index will identify either a unique permutation of output signals just added to the pool of these permutations or such a permutation which already contained in the pool. The length of the unique permutation of output signals is equal to the length of the unique probabilities list in normal form.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
One of the following conditions is met:
  • the value of sig_beg is greater than or equal to a value (incremented by 1) of the last signal of the list of weights;
  • the value of sig_end is greater than the total number of signals of the actor;
  • the sum of elements in array weight_p, which correspond to output signals of the actor within a range specified using sig_beg and sig_end, is not finite.

QSMM_ERR_WEIGHT
A negative or a non-finite element, which corresponds to an output signal of the actor, was encountered in array weight_p within a range specified using sig_beg and sig_end.
QSMM_ERR_NOCHOICE
Array weight_p does not contain at least one positive element that corresponds to an output signal of the actor within a range specified using sig_beg and sig_end.
QSMM_ERR_MPROF
No room in the pool of probabilities lists in normal form, the size of which was specified when creating the actor.
QSMM_ERR_STORAGE
Failure of statistics storage of a large actor. This could leave the actor in indeterminate state. If the actor is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the actor's state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the actor's memory in indeterminate state. If the actor's memory is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the actor's memory state will become determinate.

Function qsmm_actor_profile_add may operate slowly, which is especially true for a large actor when a Huffman tree is created. To speed up preloading a number of probability profiles, to which the same sorted list of weights of output signals corresponds, one can perform the following procedure.

  1. Preload the first probability profile using function qsmm_actor_profile_add, which returns an index of probabilities list in normal form and an index of the permutation of output signals that identify the preloaded profile.
  2. For every other probability profile, sort a list of weights of output signals, which corresponds to the profile (all profiles have the same sorted form of that list), to obtain a permutation of output signals. Actually, it is necessary to sort a list of pairs, each consisting of weight and output signal identifier, in ascending order of weights.
  3. Register every such permutation of output signals using function qsmm_actor_permut_add (described below) to obtain an index of unique permutation of output signals, which can be used along with an index of probabilities list in normal form, obtained at step 1, to identify a preloaded probability profile other than the first one.
— Function: int qsmm_actor_permut_add (qsmm_actor_t actor, int sz, const qsmm_sig_t *sig_p)

This function adds a permutation of output signals of actor to a pool for these permutations, which corresponds to the actor, if the pool does not already contain this permutation. The permutation is specified by array sig_p of length sz and must be a subset of identifiers of output signals of the actor.

On success, the function returns a non-negative index of a permutation of output signals just added to the pool or which already contained in the pool. That index uniquely identifies the permutation in the pool. On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of sz is less than 1, or array sig_p contains duplicate elements or an element, which is not an identifier of output signal of the actor.
QSMM_ERR_NOMEM
There was not enough memory to add the permutation of output signals to the pool.

The purpose of the following functions is to retrieve or set up a correspondence between an action choice state and a list of weights of output signals, which can be emitted in that action choice state.

— Function: int qsmm_get_actor_ngram_profile (qsmm_actor_t actor, int rez1, int *profile_p, int *permut_p, const qsmm_sig_t *sig_ngram_p)

This function retrieves a correspondence between an action choice state of an actor and a list of weights of output signals, which can be emitted in that action choice state. The list of weights is specified by an index of probabilities list in normal form and an index of the permutation of output signals. The action choice state is defined by list of signal identifiers sig_ngram_p of length specified in field ngram_sz of structure qsmm_actor_desc_s when creating the actor. Argument rez1 is reserved for future use and must be equal to 0.

If profile_p is not 0, then the function will set *profile_p to an index of probabilities list in normal form that corresponds to the list of weights of output signals. If permut_p is not 0, then the function will set *permut_p to an index of the permutation of output signals, which corresponds to the list of weights of output signals. If the function sets *profile_p and *permut_p to -1, then this will be an indication that the default list of weights of output signals has been assigned to the action choice state. In such a list, all output signals of the actor have equal weights, with the exception that for a large actor actual weights that follow from the structure of a Huffman tree are all equal only if the length of the list is a positive integer power of arity of the tree.

On success, the function returns a non-negative value. If array sig_ngram_p specifies an invalid action choice state n-gram, then negative error code QSMM_ERR_NGRAM will be returned.

— Function: int qsmm_set_actor_ngram_profile (qsmm_actor_t actor, int rez1, int profile, int permut, const qsmm_sig_t *sig_ngram_p)

This function sets up a correspondence between an action choice state of an actor and a list of weights of output signals, which can be emitted in that action choice state. The list of weights is specified by profile, which is an index of probabilities list in normal form, and by permut, which is an index of the permutation of output signals. The action choice state is defined by list of signal identifiers sig_ngram_p of length specified in field ngram_sz of structure qsmm_actor_desc_s when creating the actor. Argument rez1 is reserved for future use and must be equal to 0.

A special combination of profile and permut both equal to -1 is an indication to assign a default list of weights of output signals to the action choice state. The default list specifies equal weights for all output signals of the actor, with the exception that, for a large actor, actual weights that follow from the structure of a Huffman tree are all equal only if the length of the default list is a positive integer power of arity of the tree.

If the actor is the small one, the function keeps intact statistics collected for the action choice state during actor operation, which is held in storage of the actor. If the actor is the large one, the function also keeps intact statistics held in storage, a handle to which can be retrieved by function qsmm_get_actor_storage. However, the function destroys a Huffman tree that may have been created for the action choice state, in which another part of statistics collected for that state during actor operation is stored.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Argument profile is not a valid index of probabilities list in normal form or argument permut is not a valid index of the permutation of output signals and, at the same time, profile and permut are not both equal to -1, or the length of a probabilities list in normal form specified by profile is not equal to the length of a permutation of output signals specified by permut.
QSMM_ERR_NGRAM
Argument sig_ngram_p specifies an invalid action choice state n-gram. To determine the validity of an action choice state n-gram, it is checked for accordance with allowed ranges of signal identifiers specified using field range_sig_p of structure qsmm_actor_desc_s when creating the actor.
QSMM_ERR_NOSYS
The actor is the small one, and the total number of possible action choice state n-grams, calculated on the basis of allowed ranges of signal identifiers specified using field range_sig_p of structure qsmm_actor_desc_s when creating the actor, exceeds INT_MAX.
QSMM_ERR_STORAGE
Failure of statistics storage of a large actor. This could leave the actor in indeterminate state. If the actor is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the actor's state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the actor's memory in indeterminate state. If the actor's memory is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the actor's memory state will become determinate.


Next: , Previous: Specifying Weights of Output Signals, Up: Optimal Action Generation Engine

2.11 Automatic Spur

Automatic spur is an important concept related to an actor. This concept corresponds to the idea that an intrinsic feature of intelligent system would be adherence to the principle of minimum energy (or maximum probability). The use of the automatic spur may substantially increase the efficiency of actor operation and allow application of an actor in cases when there are no explicit spur increments supplied to it.

Automatic spur is especially useful when output signals of an actor represent its internal states and each action choice state is a superposition of the previous internal state of the actor and an input signal received when the actor was in that previous internal state. That is, the actor operates as a finite automaton, which has a certain number of internal states, and current internal state changes on the basis of the previous internal state and an input signal received. In this case, automatic spur is a measure of adequacy of a model of internal states within the actor to a sequence of input signals received.

In QSMM, the common approach to building a state model for identifying internal states of a (possibly abstract) entity to solve a problem is the use of a spur scheme with two spur types, which correspond to negative (inhibitory) spur and positive (excitatory) spur that countervail each other. Negative spur is the automatic spur, which typically has a normal way of perception. Positive spur is the user-supplied one, specific to the problem to be solved.

If is used by an actor, automatic spur is equal to the sum of automatic spur increments calculated within function qsmm_actor_reg_sig_in for action choice states that occur in the event history during actor operation for which prior occurrences in the event history exist. If the prior occurrence of an action choice state exists, then within function qsmm_actor_reg_sig_in there will be known an output signal that was emitted in the action choice state when it had occurred the previous time. (For the last occurrence, an output signal emitted in the action choice state is not yet known in qsmm_actor_reg_sig_in.)

An automatic spur increment is calculated for a pair that represents logical consequence hz and consists of action choice state h and output signal z emitted in that action choice state. The automatic spur increment is equal to the natural logarithm of the probability of occurrence of that pair in the event history:

f_auto_spur_incr.png

Here ν(h,z) is the number of occurrences of the pair in the event history since the beginning of actor operation, t0 is the discrete time of the previous occurrence of the pair in the event history (recorded when function qsmm_actor_reg_sig_action was called), and 0<K≤1 is the mean number of output signals per one signal in the event history.

When an actor uses one spur type at all, which corresponds to the automatic spur, it is recommended to set the type of relative probability function for the actor to QSMM_RELPROB_BUILTIN2 or QSMM_RELPROB_BUILTIN3. However, it is also recommended using the automatic spur in pair with another, countervailing spur, which measures progress in solving the problem in some other way.

To query or set an actor spur type, which corresponds to the automatic spur, functions described below can be used. By default, automatic spur is not used by an actor created by function qsmm_actor_create, except that the automatic spur is used by an implicitly created small actor associated with a large actor.

— Function: int qsmm_get_actor_auto_spur_type (qsmm_actor_t actor)

This function returns the non-negative index of a spur type, which corresponds to the automatic spur used by an actor, or negative error code QSMM_ERR_NOTFOUND if the actor does not use the automatic spur.

— Function: int qsmm_set_actor_auto_spur_type (qsmm_actor_t actor, int spur_type)

This function sets to spur_type a spur type that corresponds to the automatic spur used by actor. Spur types have zero-based indices. If spur_type is equal to -1, then the actor will not use the automatic spur (this will not affect the use of the automatic spur by a small actor associated with the large actor).

On success, the function returns a non-negative value. If spur_type is less than -1 or is greater than or equal to the number of spur types specified when creating the actor, then negative error code QSMM_ERR_INVAL will be returned.

To query or set the value of K, the following functions can be used.

— Function: double qsmm_get_actor_naction_per_evt (qsmm_actor_t actor)

This function returns a number greater than 0 and less than or equal to 1, which specifies the mean number of output signals per one signal in the event history of actor.

— Function: int qsmm_set_actor_naction_per_evt (qsmm_actor_t actor, double val)

This function sets the mean number of output signals per one signal in the event history of actor to val.

On success, the function returns a non-negative value. If val is not a finite number, or is a number less than or equal to 0, or is a number greater than 1, then negative error code QSMM_ERR_INVAL will be returned.

Function qsmm_actor_create initializes the mean number of output signals per one signal in (future) event history of a newly created actor to 0.5, which means that every second signal in the event history of the actor is expected to be an output signal.

A small actor can use at most one spur type that corresponds to the automatic spur. Functions qsmm_get_actor_auto_spur_type and qsmm_set_actor_auto_spur_type called for a small actor retrieve and set the index of a spur type, which corresponds to that automatic spur.

A large actor can use at most two spur types that correspond to the automatic spur. Automatic spur of the first type is used by a small actor associated with the large actor. Increments of that spur are calculated for action choice states, which correspond to nodes of Huffman trees traversed when generating output signals by the large actor. Increments of the automatic spur of the second type are calculated for action choice states from the event history of the large actor. Functions qsmm_get_actor_auto_spur_type and qsmm_set_actor_auto_spur_type called for a large actor retrieve and set the index of a spur type that corresponds to the latter automatic spur.

Historically, a large actor is created with the automatic spur of the first type turned on by default. A developer may want to turn off the automatic spur of the first type and make the large actor use automatic spur of the second type instead. To turn off the use of automatic spur of the first type by large actor actor_large, write lines of code

     qsmm_set_actor_auto_spur_type(
         qsmm_get_actpair_actor_env(
             qsmm_get_actpair(qsmm_get_actor_large_model(actor_large))),
         -1);

To conserve memory, a spur type, which corresponds to this automatic spur, can be reused for another purpose. For a small actor associated with the large actor, this spur type has index 0. Keep in mind that by default, the small actor uses discrete time to compute the increment velocity for this spur type. For the large actor, this spur type has special index -1.


Next: , Previous: Automatic Spur, Up: Optimal Action Generation Engine

2.12 Controlling Random Behavior

A random number generator used by an actor, either supplied when creating the actor or, if not supplied, an instance of default random number generator allocated automatically, can be obtained using the following function.

— Function: qsmm_rng_t qsmm_get_actor_rng (qsmm_actor_t actor)

This function returns the handle of a random number generator used by actor when producing actions. This function never returns 0.

The handle of a random number generator returned is typically used to seed the generator after creating the actor. See Random Number Generators, for information on how to seed a random number generator and perform other operations on it.

A useful approach to testing the efficiency of operation of an intelligent system developed using the QSMM framework is comparing a measure of efficiency of normal operation of the system with a measure of efficiency of operation of the system when actors, which are parts of the system, behave almost completely randomly. The greater is the difference between those values the more optimization mechanism provided by the actors increases overall optimality of system operation.

To query the current mode of behavior of an actor and to switch the actor to nonoptimal or normal behavior, the following functions can be used.

— Function: int qsmm_get_actor_random (qsmm_actor_t actor)

This function returns a positive value if actor is currently in the nonoptimal behavior mode or zero value if the actor is in the optimal (normal) behavior mode. This function never returns negative values.

— Function: void qsmm_set_actor_random (qsmm_actor_t actor, int flag)

This function switches the current mode of behavior of an actor to the nonoptimal or the optimal (normal) mode. If flag is non-zero, then the current mode will be switched to the nonoptimal one. If flag is zero, then the current mode will be switched to the optimal one.

For a small actor, a nonoptimal or optimal mode of behavior affects how function qsmm_actor_calc_action_prob calculates probabilities of types QSMM_PROB_AGGR and QSMM_PROB_LEARNT. When the mode is the nonoptimal one, equal probabilities are generated, multiplied by output signal weights, and normalized; when the type of probabilities is QSMM_PROB_AGGR, a probability profile, if it exists in actor's statistics storage, is also applied to the internal array that holds probabilities.

For a large actor, a nonoptimal or optimal mode of behavior is actually a mode used by a small actor associated with the large actor.

Function qsmm_actor_create initializes the current mode of behavior of a newly created actor to the optimal (normal) one.


Next: , Previous: Controlling Random Behavior of an Actor, Up: Optimal Action Generation Engine

2.13 Other Parameters of an Actor

When using a custom function, which returns a relative probability of output signal choice, or providing coherent operation of a stack of actors, it may be necessary to know a discrete time period of the last cycle processed by an actor or the mean discrete time period of all cycles processed by the actor since the beginning of its operation. This information can be obtained using the following functions.

— Function: long qsmm_get_actor_discrete_cycle_period_last (qsmm_actor_t actor)

This function returns a discrete time period of the last cycle registered by actor in function qsmm_actor_reg_sig_in. The returned value is always non-negative.

If function qsmm_actor_reg_sig_in is not yet called, then zero will be returned. When function qsmm_actor_reg_sig_in is called at least once, zero will be returned if at the last call to qsmm_actor_reg_sig_in a cycle is not encountered.

— Function: double qsmm_get_actor_discrete_cycle_period_mean (qsmm_actor_t actor)

This function returns the mean discrete time period of all cycles registered by actor in function qsmm_actor_reg_sig_in since the beginning of actor operation. If there are no cycles registered yet, then zero will be returned. The function returns only finite and non-negative values.

A function, which calculates a relative probability of output signal choice, typically uses a parameter equal to the number of actor's output signals to perform the calculation. Function qsmm_actor_create creates an actor with a particular number of output signals. However, during operation of a small actor, its output signal weights can be changed using function qsmm_set_actor_sig_weight or by assigning preloaded probability profiles to action choice states using function qsmm_set_actor_ngram_profile, and those weights may correspond to a lesser number of output signals. For example, if the actor had four output signals, and the weight of one of them were set to zero, then the actual number of output signals would be three. The same applies to a probability profile that can be specified for an action choice state (in actor's statistics storage for a small actor or using function qsmm_set_actor_ngram_profile for a large actor): if the profile probability of an output signal is equal to zero, then the actual number of output signals will be less than the number of output signals defined when creating the actor.

However, the concept of the actual number of output signals is somewhat weak. The weakness shows up when positive weights (or profile probabilities) of output signals are not equal. In this case, the actual number of output signals might not be simply the number of output signals that have positive weights (or profile probabilities). For example, if four signals have weights <0.33, 0.33, 0.33, 0.01>, then the actual number of output signals will rather be three than four.

When loading an assembler program into a node or changing values of controlled probability variables of a node, the QSMM package uses the following algorithm for the approximate calculation of the actual number of output signals, which corresponds to a list of weights.

  1. Zero c, the resulting number of output signals.
  2. Calculate a, the arithmetic mean of all positive weights of output signals.
  3. For every output signal weight b, if b>a, then add 1 to c, otherwise add b/a to c.

During actor operation, when calculating relative probabilities of emitting output signals in action choice states, to different action choice states there may correspond different actual numbers of output signals. If a function, which returns a relative probability of output signal choice, uses only one parameter that denotes the number of output signals, there may arise a need to introduce the concept of the mean number of actor's output signals, which is, in fact, weaker than the concept of the actual number of output signals. The mean number of output signals of an actor could be calculated on the basis of actual numbers of output signals from which the actor chooses a specific output signal each time it produces an action, i.e. on the basis of actual numbers of output signals for particular occurrences of action choice states in the event history, and might be the arithmetic or geometric mean of those actual numbers of output signals.

The mean number of output signals can be retrieved or set using the following functions.

— Function: double qsmm_get_actor_nsig_ctrl (qsmm_actor_t actor)

This function returns the mean number of output signals of actor. It is either the number of output signals calculated from the contents of structure qsmm_actor_desc_s, passed to function qsmm_actor_create when creating the actor, or the number of output signals previously set using function qsmm_set_actor_nsig_ctrl. Function qsmm_get_actor_nsig_ctrl can be called from a custom function that returns a relative probability of output signal choice if the function uses a formula that involves the number of output signals. A value returned by function qsmm_get_actor_nsig_ctrl is always finite and is greater than or equal to 2.

— Function: int qsmm_set_actor_nsig_ctrl (qsmm_actor_t actor, double val)

This function sets the mean number of output signals of actor to val. The mean number of output signals is used by built-in functions, which return a relative probability of output signal choice, and can be used by a custom relative probability function supplied by a developer.

On success, the function returns a non-negative value. If val is not a finite number or is a number less than 2, then negative error code QSMM_ERR_INVAL will be returned.

In the simplest case, when output signal weights are not changed during actor operation and profile probabilities are not used, there is no need to modify the mean number of output signals, which was initially set by function qsmm_actor_create. Otherwise, a program, which uses a small actor, should calculate in some way the mean number of output signals and set that number for the actor by function qsmm_set_actor_nsig_ctrl.

If an array of signal weights or a probability profile specifies that only one signal can be chosen (i.e. if the array of signal weights contains a single positive element or if a probability profile for an action choice state contains a single positive probability of an output signal), then the best will be not to call functions qsmm_actor_shl_sig, qsmm_actor_reg_sig_in, and qsmm_actor_reg_sig_action at all. In this case, action choice can be performed in a deterministic way (without using the actor), and discrete time shall not be incremented. However, if functions qsmm_actor_shl_sig and qsmm_actor_reg_sig_in were already called, then for proper accumulation of statistics for the action choice state, it would be appropriate to register that the only allowed output signal using a call to qsmm_actor_reg_sig_action and then to backtrack discrete time (using several calls to qsmm_actor_shr_sig) to the moment before feeding the action choice state n-gram to the actor.

The historical limitation for the minimum mean number of actor's output signals, which can be set using function qsmm_set_actor_nsig_ctrl, is 2. When computing the mean number of output signals on the basis of actual numbers of output signals for particular occurrences of action choice states in the event history, if there is no signal, which is only allowed to be emitted in an action choice state, and the actual number of output signals computed for an array of signal weights or a probability profile is less than 2, then you may either use 2 for the actual number of output signals for the particular occurrence of action choice state to calculate the mean number of output signals or change the array of signal weights or the probability profile in such a way that they will give more freedom for the actor to choose output signals.

To calculate the mean number of output signals, when an action choice state has a probability profile specified, the precomputed actual number of output signals can be fetched from the probability profile. You still need to store the actual number of output signals, computed in advance, in the profile. To fetch the precomputed actual number of output signals from a probability profile of an action choice state and to determine an output signal if the profile allows only one output signal to be emitted, the following function can be used.

— Function: int qsmm_get_actor_profile_nsig_ctrl (qsmm_actor_t actor, int *nsig_pos_p, double *nsig_ctrl_p, qsmm_sig_t *sig_action_p)

This function retrieves information associated with a probability profile for current action choice state of an actor held in its statistics storage. The current action choice state is the one, which corresponds to the contents of a window that holds current n-gram from the event history.

If nsig_pos_p is not 0, then the function will set *nsig_pos_p to the number of output signals, which can be emitted by the actor in its current action choice state and which have positive profile probabilities assigned. If nsig_pos_p is not 0, and current action choice state does not have a probability profile specified, then *nsig_pos_p will be set to 0.

If nsig_ctrl_p is not 0, then the function will set *nsig_ctrl_p to the actual number of output signals that can be emitted by the actor in its current action choice state. If nsig_ctrl_p is not 0, and current action choice state does not have a probability profile specified, then *nsig_ctrl_p will be set to 0.

If the probability profile allows only one output signal to be emitted, then *nsig_pos_p will be set to 1 (if nsig_pos_p is not 0), *nsig_ctrl_p will be set to 1 (if nsig_ctrl_p is not 0), and *sig_action_p will be set to that output signal (if sig_action_p is not 0). If sig_action_p is not 0, and current action choice state does not have a probability profile specified or the probability profile does not allow a single output signal to be emitted, then *sig_action_p will be set to QSMM_SIG_INVALID.

See Structures for Accessing Storage, for information on how to pass the number of output signals, which have positive profile probabilities assigned, the actual number of output signals, and a single output signal, which can be emitted (if it exists), to storage access functions to write them to statistics storage, which handle can be obtained using function qsmm_get_actor_storage, so these parameters can be retrieved later by function qsmm_get_actor_profile_nsig_ctrl.

If current action choice state has a probability profile specified, then the function will return a positive value. If current action choice state does not have a probability profile specified, then the function will return 0.

On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_NGRAM
The contents of an actor window, which holds current n-gram from the event history, correspond to an invalid action choice state. To determine the validity of an action choice state, it is checked for accordance with allowed ranges of signal identifiers specified using field range_sig_p of structure qsmm_actor_desc_s when creating the actor.
QSMM_ERR_STORAGE
Statistics storage failure.
QSMM_ERR_NOMEM
A statistics storage access function did return out of memory error.


Previous: Other Parameters of an Actor, Up: Optimal Action Generation Engine

2.14 Example of Using the Actor API

In the example of using the Actor API, an agent controlled by the sample program has to find a short path to the gold in a labyrinth and then to an exit from the labyrinth. The image of the labyrinth is encoded in the sample program using a subset of ASCII characters that look like pseudographics. You can change the image to test the behavior of the agent for various configurations of the labyrinth.

To find a resulting path, the agent visits the labyrinth the number of times defined by macro NVISIT. A visit is considered finished when the agent moves to a cell where the labyrinth exit is located. A simulated annealing approach is used, in which the temperature of the actor gradually decreases. Macro KT_0 defines the initial temperature of the actor, and macro KT_1 defines the final temperature.

At the last visit of the labyrinth, the sample program shows the image of the labyrinth and movements of the agent in it, prints an indication whether the agent took the gold, and displays current length of visiting path. After the agent finishes the last visit of the labyrinth, a user can press <Q> to exit from the sample program, small <G> to print the map of learned directions of movements from every cell of the labyrinth (reachable from the labyrinth entry) to a cell with the gold, or capital <G> to print the map of learned directions of movements from every cell of the labyrinth to the labyrinth exit after taking the gold. When the user presses <Q>, the program prints the number of visits of the labyrinth for which the agent took the gold and exits.

A random seed can be specified by a program argument. If the random seed is non-negative, then the actor will operate normally. If the random seed is negative, then the actor will generate output signals completely randomly. You could compare the behavior of the agent for these two modes of program execution.

Each labyrinth cell is a rectangle 4x3. Maximum zero-based indices of a column and a row of labyrinth cell are defined by macros MAX_X and MAX_Y. Coordinates of the entry cell of the labyrinth relative to its upper left corner are defined by macros ENTRY_X and ENTRY_Y. Characters ‘*’ at cell corners denote a cell with the gold. If there are several such cells, then visiting any of them will be considered as taking the gold, which is provided in a single copy per visit of the labyrinth. Characters ‘#’ at cell corners denote a labyrinth exit. A labyrinth can have more than one exit.

Space characters denote allowed movement paths, and other characters denote places to which movement is impossible. If a cell is reachable from the labyrinth entry, then it must have two spaces in the middle. Every reachable cell must also have two spaces or two non-spaces at the top and at the bottom (in the middle), which indicates whether movement is allowed to an adjacent cell located at the north and/or the south. When modifying the picture of the labyrinth, make sure that cells are properly aligned and connected, and there are no paths outside of the labyrinth, otherwise an assertion failure will occur.

The source code of the example is provided in file samples/labyr2.c in the package distribution and is also given below. Command make builds the sample program if the QSMM package is configured by the configure script to use the Curses library. See file INSTALL at the root of the package distribution, for information on the configure script.

     #include <assert.h>
     #include <math.h>
     #include <stdlib.h>
     #include <string.h>
     #include <unistd.h>
     
     #if defined(HAVE_CURSES_H)
     #   include <curses.h>
     #elif defined(HAVE_NCURSES_CURSES_H)
     #   include <ncurses/curses.h>
     #endif
     
     #include <qsmm/qsmm.h>
     
     #define NVISIT  200
     #define MAX_X   14
     #define MAX_Y   14
     #define ENTRY_X 0
     #define ENTRY_Y 14
     #define NSIG_IN (2*(MAX_X+1)*(MAX_Y+1))
     #define KT_0    NVISIT
     #define KT_1    1.0
     
     
     #define CHK_FAIL(func, ...)                                               \
         do {                                                                  \
             int rc=func(__VA_ARGS__);                                         \
             if (rc<0) {                                                       \
                 fprintf(stderr, #func ": %s\n", qsmm_err_str(rc));            \
                 goto Exit;                                                    \
             }                                                                 \
         }                                                                     \
         while (0)
     
     
     enum direct_e {
         DIRECT_NORTH=0,
         DIRECT_EAST =1,
         DIRECT_SOUTH=2,
         DIRECT_WEST =3
     };
     
     
     static long state_fq[3][MAX_Y+1][MAX_X+1];
     static qsmm_actor_t actor=0;
     
     
     static int show_gradient(char is_gf);
     
     
     static int opaque_maze(enum direct_e direct) {
         static const char *picture[]={
             // 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14
             "+--------+-----+--------------------------#..#",
             "|        |     |                             |",  // 0
             "|  *  *  |     |                          #  #",
             "|        |     |                             |",  // 1
             "|  *  *  |     |     +-----+                 |",
             "|        |     |     |     |                 |",  // 2
             "|        |     |     |     |     +-----+     |",
             "|        |     |     |     |     |     |     |",  // 3
             "|        |     |     |     |     |     |     |",
             "|        |     |     |     |     |     |     |",  // 4
             "|        +-----+     |     |     |     |     |",
             "|                    |     |     |     |     |",  // 5
             "|                    |     |     |     |     |",
             "|                    |     |     |     |     |",  // 6
             "|  +-----------+     +-----+     |     |     |",
             "|  |           |                 |     |     |",  // 7
             "|  |           |                 |     |     |",
             "|  |           |                 |     |     |",  // 8
             "|  +-----------+     +-----+     |     |     |",
             "|                    |     |     |     |     |",  // 9
             "|                    |     |     |     |     |",
             "|                    |     |     |     |     |",  // 10
             "+--------------+     |     |     +-----+     |",
             "|              |     |     |                 |",  // 11
             "|              |     |     |                 |",
             "|              |     |     |                 |",  // 12
             "+--------------+     +-----+     +-----+     |",
             "|                                |     |     |",  // 13
             "|                                |     |     |",
             "|                                |     |     |",  // 14
             "+..+-----------------------------+-----+-----+"
         };
         static char is_gf=0;
         static int path_len, visit=0, xx=-1, yy=-1;
         int rc, col, row, result=0;
         if (xx<0 || yy<0) {
             if (visit==NVISIT-1) {
                 if (!initscr()) exit(2);
                 noecho();
                 for (row=0; row<(MAX_Y+1)*2+1; row++)
                     mvaddstr(row,0,picture[row]);
             }
             xx=ENTRY_X;
             yy=ENTRY_Y;
             path_len=0;
         }
         assert(xx>=0 && xx<=MAX_X);
         assert(yy>=0 && yy<=MAX_Y);
         col=xx*3+1;
         row=yy*2+1;
         assert(picture[row][col]==' ' && picture[row][col+1]==' ');
         assert((picture[row-1][col]==' ' && picture[row-1][col+1]==' ') ||
                (picture[row-1][col]!=' ' && picture[row-1][col+1]!=' '));
         assert((picture[row+1][col]==' ' && picture[row+1][col+1]==' ') ||
                (picture[row+1][col]!=' ' && picture[row+1][col+1]!=' '));
         switch (direct) {
             case DIRECT_NORTH:
                 if (picture[row-1][col]!=' ' || picture[row-1][col+1]!=' ')
                     return 3;
                 yy--;
                 break;
             case DIRECT_EAST:
                 if (picture[row][col+2]!=' ') return 3;
                 xx++;
                 break;
             case DIRECT_SOUTH:
                 if (picture[row+1][col]!=' ' || picture[row+1][col+1]!=' ')
                     return 3;
                 yy++;
                 break;
             case DIRECT_WEST:
                 if (picture[row][col-1]!=' ') return 3;
                 xx--;
                 break;
             default:
                 assert(0);
         }
         path_len++;
         if (visit==NVISIT-1) mvaddstr(row,col,"  ");
         col=xx*3+1;
         row=yy*2+1;
         state_fq[2][yy][xx]++;
         state_fq[(int) is_gf][yy][xx]++;
         if (visit==NVISIT-1) {
             char ss[128];
             int picture_w=strlen(picture[0]);
             mvaddstr(row,col,"[]");
             sprintf(ss," Gold found: %d",is_gf);
             mvaddstr(1,picture_w+2,ss);
             sprintf(ss,"Path length: %d",path_len);
             mvaddstr(3,picture_w+2,ss);
             move((MAX_Y+1)*2+3,0);
             refresh();
             usleep(125000);
         }
         if (picture[row-1][col-1]=='*' && picture[row-1][col+2]=='*' &&
             picture[row+1][col-1]=='*' && picture[row+1][col+2]=='*') {
             if (!is_gf) {
                 is_gf=1;
                 result=1;
             }
         }
         else if (picture[row-1][col-1]=='#' && picture[row-1][col+2]=='#' &&
                  picture[row+1][col-1]=='#' && picture[row+1][col+2]=='#') {
             is_gf=0;
             result=2;
             xx=-1;
             yy=-1;
             if (visit==NVISIT-1) {
                 int sel=-1;
                 while (1) {
                     row=(MAX_Y+1)*2+3;
                     mvaddstr(row,2,"[g] - gradient before taking the gold");
                     mvaddstr(row+1,2,"[G] - gradient after taking the gold");
                     mvaddstr(row+2,2,"[Q] - quit");
                     rc=getch();
                     if (sel>=0) mvaddstr(row+sel,0," ");
                     if (rc=='q' || rc=='Q') break;
                     switch (rc) {
                         case 'g': sel=0; show_gradient(0); break;
                         case 'G': sel=1; show_gradient(1); break;
                     }
                     mvaddstr(row+sel,0,">");
                 }
                 endwin();
             }
             else visit++;
         }
         return result;
     }
     
     
     static int show_gradient(char is_gf) {
         int ii, xx, yy, result=-1;
         qsmm_sig_t *sig_ngram_p=qsmm_get_actor_sig_ngram(actor);
         assert(!is_gf || is_gf==1);
         sig_ngram_p[0]=is_gf;
         for (yy=0; yy<=MAX_Y; yy++) {
             sig_ngram_p[2]=yy;
             for (xx=0; xx<=MAX_X; xx++) {
                 const char *ccp=0;
                 double prob_max=-1, *prob_p;
                 enum direct_e direct=DIRECT_NORTH;
                 sig_ngram_p[1]=xx;
                 if (state_fq[(int) is_gf][yy][xx]<1) continue;
                 CHK_FAIL(qsmm_actor_calc_action_prob, actor,
                          0, NSIG_IN, NSIG_IN+4, QSMM_PROB_LEARNT);
                 prob_p=qsmm_get_actor_choice_sig_prob(actor);
                 for (ii=0; ii<4; ii++) {
                     double prob=prob_p[NSIG_IN+ii];
                     if (prob_max>=prob) continue;
                     prob_max=prob;
                     direct=ii;
                 }
                 qsmm_actor_choice_sig_prob_release(actor);
                 switch (direct) {
                     case DIRECT_NORTH: ccp="^ "; break;
                     case DIRECT_EAST:  ccp="> "; break;
                     case DIRECT_SOUTH: ccp="v "; break;
                     case DIRECT_WEST:  ccp="< "; break;
                 }
                 mvaddstr(yy*2+1,xx*3+1,ccp);
             }
         }
         result=0;
     
     Exit:
         return result;
     }
     
     
     int main(int argc, char **argv) {
         int rc, visit, path_len=0, n_gold_found=0, seed=0, exit_code=1;
         double ktemperature=KT_0, mut=pow(KT_1/KT_0,1.0/NVISIT);
         struct qsmm_pair_sig_s range_sig[3];
         struct qsmm_actor_desc_s actor_desc;
         struct qsmm_actor_sig_spec_in_out_s *iop=&actor_desc.sig_spec.in_out;
         memset(range_sig,0,sizeof(range_sig));
         range_sig[0].second=1;
         range_sig[1].second=MAX_X;
         range_sig[2].second=MAX_Y;
         memset(&actor_desc,0,sizeof(actor_desc));
         actor_desc.nspur=1;
         actor_desc.ngram_sz=3;
         actor_desc.compat=1;
         actor_desc.sig_spec_type=QSMM_ACTOR_SIG_SPEC_IN_OUT;
         actor_desc.range_sig_p=range_sig;
         iop->nsig_in=NSIG_IN;
         iop->nsig_out=4;
         CHK_FAIL(qsmm_actor_create,&actor_desc,&actor);
         CHK_FAIL(qsmm_set_actor_naction_per_evt,actor,0.25);
         if (argc>1) seed=atoi(argv[1]);
         if (seed<0) qsmm_set_actor_random(actor,1);
         qsmm_rng_seed(qsmm_get_actor_rng(actor),abs(seed));
         for (visit=0; visit<NVISIT; visit++) {
             char is_gf=0;
             int xx=ENTRY_X, yy=ENTRY_Y;
             CHK_FAIL(qsmm_set_actor_ktemperature,actor,ktemperature);
             while (1) {
                 qsmm_sig_t sig_action=QSMM_SIG_INVALID;
                 enum direct_e direct;
                 CHK_FAIL(qsmm_actor_shl_sig,actor,is_gf,0);
                 CHK_FAIL(qsmm_actor_shl_sig,actor,xx,0);
                 CHK_FAIL(qsmm_actor_reg_sig_in,actor,yy);
                 CHK_FAIL(qsmm_actor_calc_action_prob, actor,
                          0, NSIG_IN, NSIG_IN+4, QSMM_PROB_LEARNT);
                 CHK_FAIL(qsmm_get_actor_sig_action, actor,
                          0, NSIG_IN, NSIG_IN+4, &sig_action);
                 CHK_FAIL(qsmm_actor_reg_sig_action,actor,sig_action);
                 CHK_FAIL(qsmm_actor_time_delta,actor,1);
                 direct=sig_action-NSIG_IN;
                 rc=opaque_maze(direct);
                 switch (rc) {
                     case 0:
                         break;
                     case 1:
                         is_gf=1;
                         n_gold_found++;
                         break;
                     case 2:
                         if (is_gf) CHK_FAIL(qsmm_actor_spur_delta,actor,0,1);
                         break;
                     case 3:
                         continue;
                     default:
                         assert(0);
                 }
                 if (rc==2) break;
                 switch (direct) {
                     case DIRECT_NORTH: yy--; break;
                     case DIRECT_EAST:  xx++; break;
                     case DIRECT_SOUTH: yy++; break;
                     case DIRECT_WEST:  xx--; break;
                     default: assert(0);
                 }
                 path_len++;
             }
             ktemperature*=mut;
         }
         printf("\nn_gold_found=%d\n",n_gold_found);
         exit_code=0;
     
     Exit:
         qsmm_actor_destroy(actor);
         return exit_code;
     }


Next: , Previous: Optimal Action Generation Engine, Up: Top

3 Statistics Storage

Statistics storage is used to hold information on action choice states and cycle types collected over the event history since the beginning of actor operation. Additionally, statistics storage can hold probability profiles assigned to action choice states. The proper use of probability profiles may substantially increase the efficiency of operation of a system you develop. Statistics storage is automatically created for an actor by function qsmm_actor_create and destroyed by function qsmm_actor_destroy.

Storage API functions provide means to get the number of spur types supported by storage, to read and write a condition for an action choice state and statistics for a cycle type, to remove information on an action choice state from storage, to enumerate action choice states and cycle types, information on which is held in storage.

A powerful mechanism, which can be used by a developer, is setting storage redirection functions that return initial conditions for action choice states and initial statistics for cycle types when Storage API functions need this information. The mechanism can be especially helpful when specifying the same probability profile for a large number of nodes of a multinode model. Because probability profiles are stored within data structures of action choice states and cycle types, instances of those structures will not be used (and possibly allocated) until conditions for action choice states and statistics for cycle types are updated the first time. Such delayed use (and possible allocation) of the instances may reduce the memory footprint of storage and speed up program startup.


Next: , Up: Statistics Storage

3.1 Types of Storage

There are two types of statistics storage supported: flat and map. The type of statistics storage of an actor has to be specified using field use_flat_storage of structure qsmm_actor_desc_s when creating the actor by function qsmm_actor_create.

Flat storage uses preallocated arrays and does not perform memory allocation when statistics is written to storage. Therefore, flat storage operates quickly but may require a huge amount of working memory. To reduce the amount of memory occupied by flat storage of an actor, specify allowed ranges of signal identifiers in action choice state n-grams using field range_sig_p of structure qsmm_actor_desc_s as precisely as possible when creating the actor by function qsmm_actor_create.

Map storage uses binary trees to hold statistics and dynamically allocates the working memory. When map storage does not hold probability profiles, it contains information on action choice states and cycle types that did occur at least one time in the event history. In this case, at the beginning of actor operation the storage occupies a small amount of memory, which gradually increases as more action choice states and cycle types occur the first time in the event history.

Map storage has two shortcomings. The first is that a map storage operates slower than the flat storage. The second is that when a map storage holds statistics for all possible action choice states and cycle types, it occupies much more memory than the flat storage requires. The second shortcoming is not a hindrance, because not all possible action choice states and cycle types usually occur in the event history, and because the actor usually operates shorter time than it is required for a large number of action choice states and cycle types to occur the first time in the event history.

Storage of flat or map type is referred to by a storage handle.

— Data type: qsmm_storage_t

This is a type for a storage handle. It is a pointer, so variables of this type can have zero value. A storage handle can be obtained for an actor by function qsmm_get_actor_storage and passed to API functions that take argument qsmm_storage_t until the actor is destroyed.

The number of spur types supported by storage, which is represented by a storage handle, can be obtained using the following function.

— Function: int qsmm_get_storage_nspur (qsmm_storage_t storage)

This function returns the number of spur types, statistics on which can be held in storage. The returned value is always positive.


Next: , Previous: Types of Storage, Up: Statistics Storage

3.2 Structures for Accessing Storage

The following structure holds a condition for an action choice state.

— Structure: qsmm_state_s

This structure is used for storing a condition for an action choice state. It contains the following fields.

— Field: int nsig_pos

This field contains information on the number of types of cycles that start at this action choice state and have positive profile probabilities assigned. The meanings of various values of this field are given below.

0
The action choice state does not have a probability profile specified.
i>1
The number of types of cycles that start at this action choice state and have positive profile probabilities assigned. Is used to determine whether to utilize ordinary vectors or sparse vectors to store relative probabilities of output signals choice.
i<0
Only one cycle type has a positive profile probability assigned, and -i-1 is equal to an output signal, which is only allowed by the probability profile.
1
Disallowed and causes undefined behavior.

— Field: long tmd0

A non-negative value of this field indicates discrete time of emitting an output signal, which corresponds to the last cycle started at this action choice state, recorded just before emitting the output signal. A negative value indicates that there were no cycles started at this action choice state yet.

— Field: double tmc0

A non-negative value of this field indicates continuous time that corresponds to the value of field tmd0. A negative value indicates that there were no cycles started at this action choice state yet.

— Field: double nsig_ctrl

The actual number of output signals, which corresponds to a probability profile for the action choice state. See Other Parameters of an Actor, for the explanation of the concept of the actual number of output signals for an action choice state.

— Field: qsmm_sig_t sig_cycle_next

An output signal that indicates the direction of the last cycle started at this action choice state. If there are no cycles started at this action choice state yet, then the value of this field will be QSMM_SIG_INVALID.

To an action choice state there corresponds an array of instances of a structure, which holds an initial value of the spur recorded when the last cycle was started at the action choice state. The number of elements in the array is equal to the number of spur types supported by storage.

The structure contains only one field, but is designed as a structure to simplify adding to an action choice state for research purposes other information associated with a spur type. When storage prepares an instance of this structure for the first use, it initializes the instance with zeroes by function memset, so fields you may have added initially will have zero values.

The structure is described below.

— Structure: qsmm_sspur_s

This structure holds a condition associated with a spur type for an action choice state. The structure contains the following field.

— Field: double val0

A value of the spur recorded when the last cycle was started at the action choice state, i.e. at the latest time when an output signal was emitted in this action choice state.

The following structure holds statistics for a cycle type.

— Structure: qsmm_cycle_s

This structure holds statistics for a cycle type, i.e. a pair that consists of an action choice state and an output signal, which could be emitted in that action choice state. The structure contains the following fields.

— Field: long fq

The number of cycles, which belong to this cycle type, registered in the event history so far.

— Field: long period_sum_d

The total length of cycles, which belong to this cycle type, registered in the event history so far, measured in discrete time.

— Field: double period_sum_c

The total length of cycles, which belong to this cycle type, registered in the event history so far, measured in continuous time.

— Field: double profile

A profile probability—weight, by which a relative probability of output signal choice is multiplied. Is used when field nsig_pos of an instance of structure qsmm_state_s, which holds a condition for corresponding action choice state, contains a non-zero value. If is used, then the sum of profile probabilities for all cycle types for the action choice state must be equal to 1.

To a cycle type there corresponds an array of instances of a structure, which holds the sum of spur increments over cycles of that type. The number of elements in the array is equal to the number of spur types supported by storage.

The structure contains only one field, but is designed as a structure to simplify adding to a cycle type for research purposes other information associated with a spur type. When storage prepares an instance of this structure for the first use, it initializes the instance with zeroes by function memset, so fields you may have added initially will have zero values.

The structure is described below.

— Structure: qsmm_cspur_s

This structure holds statistics associated with a spur type for a cycle type. The structure contains the following field.

— Field: double delta_sum

The sum of spur increments over cycles of this type occurred in the event history so far. Each spur increment is the difference between the value of the spur at the time of cycle end and the value of the spur at the time of cycle start stored in field val0 of structure qsmm_sspur_s.

To a cycle type an action choice state and an output signal correspond. The time of cycle start is the time when in the event history there did occur that action choice state, in which that output signal was emitted. The time of cycle end is the time when the action choice state did occur again in the event history.


Next: , Previous: Structures for Accessing Storage, Up: Statistics Storage

3.3 Reading and Writing Statistics

To read and write a condition for an action choice state, the following functions can be used.

— Function: int qsmm_get_storage_state_stats (qsmm_storage_t storage, int ngram_sz, const qsmm_sig_t *sig_ngram_p, struct qsmm_state_s *state_p, struct qsmm_sspur_s *sspur_p)

This function reads from a storage a condition for an action choice state specified by list of signal identifiers sig_ngram_p of length ngram_sz. If state_p is not 0, then the condition will be copied to *state_p. If sspur_p is not 0, then conditions for spur of supported types will be copied to an array pointed by sspur_p. The array must be capable of holding the number of elements returned by function qsmm_get_storage_nspur.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NGRAM
Storage does not support holding information on an action choice state n-gram specified by arguments sig_ngram_p and ngram_sz.
QSMM_ERR_STORAGE
Storage failure. See Getting the Reason of a Storage Failure, for information on how to get the reason of the failure.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_set_storage_state_stats (qsmm_storage_t storage, int ngram_sz, const qsmm_sig_t *sig_ngram_p, const struct qsmm_state_s *state_p, const struct qsmm_sspur_s *sspur_p)

This function writes to storage a condition for an action choice state specified by list of signal identifiers sig_ngram_p of length ngram_sz. If state_p is not 0, then condition *state_p will be copied to storage. If sspur_p is not 0, then conditions for spur of supported types will be copied to storage from an array pointed by sspur_p. The number of array elements copied is returned by function qsmm_get_storage_nspur.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NGRAM
Storage does not support holding information on an action choice state n-gram specified by arguments sig_ngram_p and ngram_sz.
QSMM_ERR_STORAGE
Storage failure. See Getting the Reason of a Storage Failure, for information on how to get the reason of the failure.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To update a condition for an action choice state, you first need to read an existing condition using function qsmm_get_storage_state_stats, then set values of some fields, and finally write the condition using function qsmm_set_storage_state_stats. For example, updating parameters of a probability profile for an action choice state to new values nsig_pos and nsig_ctrl could be performed using a block of code like this:

     struct qsmm_state_s state;
     if (qsmm_get_storage_state_stats(storage, ngram_sz, sig_ngram_p,
                                      &state, 0)<0)
         goto Error;
     state.nsig_pos=nsig_pos;
     state.nsig_ctrl=nsig_ctrl;
     if (qsmm_set_storage_state_stats(storage, ngram_sz, sig_ngram_p,
                                      &state, 0)<0)
         goto Error;

To read and write statistics for a cycle type, the following functions can be used.

— Function: int qsmm_get_storage_cycle_stats (qsmm_storage_t storage, int ngram_sz, const qsmm_sig_t *sig_ngram_p, qsmm_sig_t sig_cycle_next, struct qsmm_cycle_s *cycle_p, struct qsmm_cspur_s *cspur_p)

This function reads statistics for a cycle type from storage. The cycle type is represented by an action choice state, specified by list of signal identifiers sig_ngram_p of length ngram_sz, and by a cycle direction specified by signal sig_cycle_next.

If cycle_p is not 0, then the statistics will be copied to *cycle_p. If cspur_p is not 0, then statistics for spur of supported types will be copied to an array pointed by cspur_p. The array must be capable of holding the number of elements returned by function qsmm_get_storage_nspur.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NGRAM
Storage does not support holding information on an action choice state n-gram specified by arguments sig_ngram_p and ngram_sz.
QSMM_ERR_STORAGE
Storage failure. See Getting the Reason of a Storage Failure, for information on how to get the reason of the failure.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_set_storage_cycle_stats (qsmm_storage_t storage, int ngram_sz, const qsmm_sig_t *sig_ngram_p, qsmm_sig_t sig_cycle_next, const struct qsmm_cycle_s *cycle_p, const struct qsmm_cspur_s *cspur_p)

This function writes statistics for a cycle type to storage. The cycle type is represented by an action choice state, specified by list of signal identifiers sig_ngram_p of length ngram_sz, and by a cycle direction specified by signal sig_cycle_next.

If cycle_p is not 0, then statistics *cycle_p will be copied to storage. If cspur_p is not 0, then statistics for spur of supported types will be copied to storage from an array pointed by cspur_p. The number of array elements copied is returned by function qsmm_get_storage_nspur.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NGRAM
Storage does not support holding information on an action choice state n-gram specified by arguments sig_ngram_p and ngram_sz.
QSMM_ERR_STORAGE
Storage failure. See Getting the Reason of a Storage Failure, for information on how to get the reason of the failure.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To update statistics for a cycle type, you first need to read statistics using function qsmm_get_storage_cycle_stats, then set values of some fields, and finally write statistics using function qsmm_set_storage_cycle_stats. For example, updating a profile probability4 for a cycle type to new value profile could be performed using a block of code like this:

     struct qsmm_cycle_s cycle;
     if (qsmm_get_storage_cycle_stats(storage, ngram_sz, sig_ngram_p,
                                      sig_cycle_next, &cycle, 0)<0)
         goto Error;
     cycle.profile=profile;
     if (qsmm_set_storage_cycle_stats(storage, ngram_sz, sig_ngram_p,
                                      sig_cycle_next, &cycle, 0)<0)
         goto Error;

In the current version of the package storage is used to hold statistics for action choice state n-grams of fixed length. However, as you might see, functions that read and write statistics as well as some other functions, which belong to the Storage API, take argument ngram_sz that specifies the length of action choice state n-gram. This argument is reserved for future use in the sense that in the future single storage can be used to hold statistics for action choice state n-grams of varying lengths.

To speed up program operation, Storage API functions perform minimal checks on the validity of their arguments. When the arguments have invalid values, program behavior is undefined.

To remove information on an action choice state from storage, the following function can be used.

— Function: int qsmm_storage_remove_state (qsmm_storage_t storage, int ngram_sz, const qsmm_sig_t *sig_ngram_p)

This function removes from storage information on an action choice state specified by list of signal identifiers sig_ngram_p of length ngram_sz. The information includes a condition for the action choice state and statistics for all types of cycles that start at the action choice state. For map storage, memory allocated for holding this information is freed.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
Information on an action choice state n-gram, which is specified by arguments sig_ngram_p and ngram_sz, not found in storage, nothing to remove.
QSMM_ERR_NGRAM
Storage does not support holding information on an action choice state n-gram specified by arguments sig_ngram_p and ngram_sz.
QSMM_ERR_STORAGE
Storage failure. See Getting the Reason of a Storage Failure, for information on how to get the reason of the failure.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Reading and Writing Statistics, Up: Statistics Storage

3.4 Enumerating States and Cycle Types

To enumerate action choice states, information on which is held in storage, the following function can be used.

— Function: int qsmm_storage_enum_states (qsmm_storage_t storage, int ngram_prefix_sz, const qsmm_sig_t *sig_ngram_prefix_p, qsmm_enum_states_callback_func_t callback_func, void *paramp)

This function enumerates action choice states, information on which is held in storage. Only those action choice states, which n-grams have prefix sig_ngram_prefix_p of length ngram_prefix_sz, are enumerated. If ngram_prefix_sz is 0, then sig_ngram_prefix_p can be 0.

The process of enumeration is a repeated calling callback function callback_func, to which an action choice state n-gram and user parameter paramp are passed. If the callback function returns a positive value, then the process of enumeration will be continued. If the callback function returns zero, then the process of enumeration will be terminated, and function qsmm_storage_enum_states will report success. If the callback function returns a negative value, then the process of enumeration will be terminated, and function qsmm_storage_enum_states will report failure.

In the current implementation, function qsmm_storage_enum_states does not support recursive calling from the callback function for the same storage.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_CALLBACK
The callback function did return an error.
QSMM_ERR_STORAGE
Storage failure. See Getting the Reason of a Storage Failure, for information on how to get the reason of the failure.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

The type of a pointer to a callback function, which is called for every enumerated action choice state, is described below.

— Data type: qsmm_enum_states_callback_func_t

This is a type of callback function pointer, to which the following declaration corresponds:

          typedef int (*qsmm_enum_states_callback_func_t)(
              qsmm_storage_t storage,
              int ngram_sz,
              const qsmm_sig_t *sig_ngram_p,
              void *paramp);

The callback function is called for every enumerated action choice state of storage. The list of signal identifiers of an action choice state is passed via argument sig_ngram_p, and the length of the list is passed via argument ngram_sz. A user parameter is passed via argument paramp.

The callback function might return a positive value if the process of enumeration should be continued, zero if the process of enumeration should be terminated, or a negative value on error.

To enumerate cycle types for an action choice state, i.e. output signals, where each output signal along with the action choice state makes up a cycle type, the following function can be used.

— Function: int qsmm_get_storage_cycle_next (qsmm_storage_t storage, int ngram_sz, const qsmm_sig_t *sig_ngram_p, qsmm_sig_t *sig_cycle_next_p)

This function retrieves the next cycle type, information on which is held in storage. The next cycle type is retrieved for an action choice state specified by list of signal identifiers sig_ngram_p of length ngram_sz. The next cycle type is represented by an output signal, which along with the action choice state makes up a cycle type.

When calling the function, *sig_cycle_next_p must contain an output signal for which it is necessary to obtain an output signal that corresponds to the next cycle type.

If *sig_cycle_next_p is equal to QSMM_SIG_INVALID, then on successful function completion *sig_cycle_next_p will contain an output signal that corresponds to the first cycle type for the action choice state.

If *sig_cycle_next_p is not equal to QSMM_SIG_INVALID, then on successful function completion *sig_cycle_next_p will contain an output signal greater than output signal *sig_cycle_next_p specified when calling the function, which corresponds to the next cycle type for the action choice state.

The first (or the next) output signal retrieved corresponds to the first (or to the next) cycle type, information on which is held in storage. If there is no such cycle type found, then *sig_cycle_next_p will be set to QSMM_SIG_INVALID.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NGRAM
Storage does not support holding information on an action choice state n-gram specified by arguments sig_ngram_p and ngram_sz.
QSMM_ERR_STORAGE
Storage failure. See Getting the Reason of a Storage Failure, for information on how to get the reason of the failure.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Enumerating States and Cycle Types, Up: Statistics Storage

3.5 Providing Initial Statistics

Before the first use, instances of structures qsmm_state_s, qsmm_sspur_s, qsmm_cycle_s, and qsmm_cspur_s, which hold conditions for action choice states and statistics for cycle types, are initialized by Storage API functions with zeroes with the help of function memset. For every instance of structure qsmm_state_s, the following assignments are also performed: values of fields tmd0 and tmc0 are set to -1, and the value of field sig_cycle_next is set to QSMM_SIG_INVALID.

An application program can provide functions, which will be called by Storage API functions at time of initialization of the instances, and which may perform application-specific initialization. Those functions will be called during operations of access to a condition for an action choice state or statistics for a cycle type when storage does not contain requested information about the action choice state or the cycle type.

Functions, which perform application-specific initialization, may e.g. set parameters of a probability profile for an action choice state. They may also call Storage API functions for other action choice states, e.g. to copy parameters of a probability profile from another action choice state.

— Data type: qsmm_get_state_stats_func_t

This is a type for a pointer to a redirection function that performs application-specific initialization of a condition for an action choice state. To this type the following declaration corresponds:

          typedef int (*qsmm_get_state_stats_func_t)(
              qsmm_storage_t storage,
              int ngram_sz,
              const qsmm_sig_t *sig_ngram_p,
              struct qsmm_state_s *state_p,
              struct qsmm_sspur_s *sspur_p,
              void *paramp);

Upon function call, *state_p and array sspur_p contain preinitialized values. The function can set *state_p and/or elements of array sspur_p to application-specific initial condition for an action choice state of storage. The action choice state is specified by list of signal identifiers sig_ngram_p of length ngram_sz. Elements of array sspur_p correspond to spur types supported by storage. The number of those elements is returned by function qsmm_get_storage_nspur. Argument paramp is a user parameter specified when setting the redirection function.

The function must return a non-negative value on success or one of the following negative error codes on failure.

QSMM_ERR_STORAGE
Storage failure. The storage message list is expected to contain a message that describes the reason of the failure. See Getting the Reason of a Storage Failure, for more information on storage message list.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To retrieve or set a redirection function, which performs application-specific initialization of a condition for an action choice state, the following functions can be used.

— Function: int qsmm_get_storage_state_stats_redir (qsmm_storage_t storage, qsmm_get_state_stats_func_t *get_state_stats_func_p, void **param_pp)

This function retrieves a redirection function (previously set), which performs application-specific initialization of conditions for action choice states that could be held in storage. If get_state_stats_func_p is not 0, then *get_state_stats_func_p will be set to a pointer to that redirection function or to 0 if there is no such function assigned to the storage. If param_pp is not 0, then *param_pp will be set to a user parameter of that redirection function specified when assigning the redirection function to the storage. Function qsmm_get_storage_state_stats_redir shall return a non-negative value.

— Function: int qsmm_set_storage_state_stats_redir (qsmm_storage_t storage, qsmm_get_state_stats_func_t get_state_stats_func, void *paramp)

This function sets a redirection function, which performs application-specific initialization of conditions for action choice states that could be held in storage. A pointer to the redirection function is specified by argument get_state_stats_func. A user parameter of the redirection function is specified by argument paramp. If get_state_stats_func is 0, then no redirection function, which performs application-specific initialization of conditions for action choice states, will be used for the storage. Function qsmm_set_storage_state_stats_redir shall return a non-negative value.

— Data type: qsmm_get_cycle_stats_func_t

This is a type for a pointer to a redirection function that performs application-specific initialization of statistics for a cycle type. To this type the following declaration corresponds:

          typedef int (*qsmm_get_cycle_stats_func_t)(
              qsmm_storage_t storage,
              int ngram_sz,
              const qsmm_sig_t *sig_ngram_p,
              qsmm_sig_t sig_cycle_next,
              struct qsmm_cycle_s *cycle_p,
              struct qsmm_cspur_s *cspur_p,
              void *paramp);

Upon function call, *cycle_p and array cspur_p contain preinitialized values. The function can set *cycle_p and/or elements of array cspur_p to application-specific initial statistics for a cycle type of storage. The cycle type is specified by an action choice state and output signal sig_cycle_next that indicates a cycle direction. The action choice state is specified by list of signal identifiers sig_ngram_p of length ngram_sz. Elements of array cspur_p correspond to spur types supported by storage. The number of those elements is returned by function qsmm_get_storage_nspur. Argument paramp is a user parameter specified when setting the redirection function.

The function must return a non-negative value on success or one of the following negative error codes on failure.

QSMM_ERR_STORAGE
Storage failure. The storage message list is expected to contain a message that describes the reason of the failure. See Getting the Reason of a Storage Failure, for more information on storage message list.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To retrieve or set a redirection function, which performs application-specific initialization of statistics for a cycle type, the following functions can be used.

— Function: int qsmm_get_storage_cycle_stats_redir (qsmm_storage_t storage, qsmm_get_cycle_stats_func_t *get_cycle_stats_func_p, void **param_pp)

This function retrieves a redirection function, which performs application-specific initialization of statistics for cycle types that could be held in storage. If get_cycle_stats_func_p is not 0, then *get_cycle_stats_func_p will be set to a pointer to that redirection function or to 0 if there is no such function assigned to the storage. If param_pp is not 0, then *param_pp will be set to a user parameter of that redirection function specified when assigning the redirection function to the storage. Function qsmm_get_storage_cycle_stats_redir shall return a non-negative value.

— Function: int qsmm_set_storage_cycle_stats_redir (qsmm_storage_t storage, qsmm_get_cycle_stats_func_t get_cycle_stats_func, void *paramp)

This function sets a redirection function, which performs application-specific initialization of statistics for cycle types that could be held in storage. A pointer to the redirection function is specified by argument get_cycle_stats_func. A user parameter of the redirection function is specified by argument paramp. If get_cycle_stats_func is 0, then no redirection function, which performs application-specific initialization of statistics for cycle types, will be used for the storage. Function qsmm_set_storage_cycle_stats_redir shall return a non-negative value.

When there is used a redirection function, which performs application-specific initialization of statistics for cycle types, it may be necessary to provide a redirection function, which returns the next type of cycles that start at an action choice state. The latter redirection function will provide correct enumeration of cycle types, which have profile probabilities assigned, in case of when those probabilities are supplied by external means (e.g. in a situation when an action choice state uses another action choice state as the source of probability profile).

— Data type: qsmm_get_cycle_next_func_t

This is a type for a pointer to a redirection function, which returns the next type of cycles that start at an action choice state. To this type the following declaration corresponds:

          typedef int (*qsmm_get_cycle_next_func_t)(
              qsmm_storage_t storage,
              int ngram_sz,
              const qsmm_sig_t *sig_ngram_p,
              qsmm_sig_t *sig_cycle_next_p,
              void *paramp);

An action choice state, information on which is held in storage, is specified by list of signal identifiers sig_ngram_p of length ngram_sz. Upon function call, *sig_cycle_next_p contains an output signal for which it is necessary to obtain an output signal that corresponds to the next cycle type. If *sig_cycle_next_p is equal to QSMM_SIG_INVALID, then on successfully performed redirection, *sig_cycle_next_p must contain an output signal that corresponds to the first cycle type for the action choice state. If *sig_cycle_next_p is not equal to QSMM_SIG_INVALID, then on successfully performed redirection, *sig_cycle_next_p must contain an output signal greater than output signal *sig_cycle_next_p passed upon the function call, which corresponds to the next cycle type for the action choice state. If such the first or the next cycle type is not found, then *sig_cycle_next_p must be set to QSMM_SIG_INVALID. Argument paramp is a user parameter specified when setting the redirection function.

If the function successfully performs the redirection and possibly updates output signal *sig_cycle_next_p, then it must return a positive value. If the function does not perform the redirection, then it must return 0, so the next cycle type will be retrieved by function qsmm_get_storage_cycle_next without the redirection. On failure, the function must return one of the following negative error codes.

QSMM_ERR_STORAGE
Storage failure. The storage message list is expected to contain a message that describes the reason of the failure. See Getting the Reason of a Storage Failure, for more information on storage message list.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To retrieve or set a redirection function, which returns the next type of cycles that start at an action choice state, the following functions can be used.

— Function: int qsmm_get_storage_cycle_next_redir (qsmm_storage_t storage, qsmm_get_cycle_next_func_t *get_cycle_next_func_p, void **param_pp)

This function retrieves a redirection function that returns the next type of cycles, which start at an action choice state and information on which is held in storage. If get_cycle_next_func_p is not 0, then *get_cycle_next_func_p will be set to a pointer to that redirection function or to 0 if there is no such function assigned to the storage. If param_pp is not 0, then *param_pp will be set to a user parameter of that redirection function specified when assigning the redirection function to the storage. Function qsmm_get_storage_cycle_next_redir shall return a non-negative value.

— Function: int qsmm_set_storage_cycle_next_redir (qsmm_storage_t storage, qsmm_get_cycle_next_func_t get_cycle_next_func, void *paramp)

This function sets a redirection function that returns the next type of cycles, which start at an action choice state and information on which is held in storage. A pointer to the redirection function is specified by argument get_cycle_next_func. A user parameter of the redirection function is specified by argument paramp. If get_cycle_next_func is 0, then no redirection function that returns the next type of cycles, which start at an action choice state, will be used for the storage. Function qsmm_set_storage_cycle_next_redir shall return a non-negative value.


Next: , Previous: Providing Initial Statistics, Up: Statistics Storage

3.6 Getting the Reason of a Storage Failure

When a Storage API function returns error code QSMM_ERR_STORAGE, the function generates one or more messages that describe an error occurred. The error messages are held in a message list associated with storage. The message list is cleared upon entry to every Storage API function that might return error code QSMM_ERR_STORAGE. See Messages and Message Lists, for more information on working with error message lists.

A message list is represented by a handle of type qsmm_msglist_t. A message list associated with storage can be obtained using the following function.

— Function: qsmm_msglist_t qsmm_get_storage_msglist (qsmm_storage_t storage)

This function returns the handle of a message list associated with storage. This function never returns 0.

When a Storage API function returns error code QSMM_ERR_STORAGE to an application program, which executable is named prg_name, the error message list can be dumped to stderr using a line of code like this:

     qsmm_msglist_dump(qsmm_get_storage_msglist(storage),prg_name,0,0,stderr);


Previous: Getting the Reason of a Storage Failure, Up: Statistics Storage

3.7 Example of Using the Storage API

When working with an actor, a helpful feature would be the ability to save the state of the actor to a file and restore that state from the file later. This feature is not built in the package, but it can be programmed by a developer manually using Actor and Storage APIs. Example functions that save and load the state of a small actor are available in file samples/load_save.c in the package distribution and are also given in this section.

The following actor parameters are not saved and loaded by the functions.

Function save_actor_state saves the state of an actor to a specified stream. It returns 0 on success, -1 on soft errors, such as out of memory error, -2 on stream write errors, and -3 if the actor is the large one.

Function load_actor_state loads the state of an actor from a specified stream. Before calling the function, an actor, which state is to be loaded and which handle is passed to the function, must be created by function qsmm_actor_create. This actor must be a small one, the actor and actor which state was saved to the stream must have the same numbers of signals and supported spur types, the same length of action choice state n-gram, the same ranges of allowed signal identifiers in action choice state n-grams, and the same sets of identifiers of output signals. The function returns 0 on success, -1 on soft errors, -2 on stream read errors, -3 on errors in read data, and -4 on incompatibility between parameters of an actor specified by an actor handle passed to the function and parameters read from the stream.

     #include <assert.h>
     #include <math.h>
     #include <stdlib.h>
     #include <string.h>
     
     #include <qsmm/qsmm.h>
     
     
     #define ERRRET(code)                                                      \
         do {                                                                  \
             result=(code);                                                    \
             goto Exit;                                                        \
         }                                                                     \
         while (0)
     
     
     #define LOAD(var)                                                         \
         do {                                                                  \
             if (fread(&var,sizeof(var),1,filep)!=1) ERRRET(-2);               \
         }                                                                     \
         while (0)
     
     
     #define SAVE(var)                                                         \
         do {                                                                  \
             if (fwrite(&var,sizeof(var),1,filep)!=1) ERRRET(-2);              \
         }                                                                     \
         while (0)
     
     
     struct param_s {
         int                 err;
         int                 nspur;
         FILE                *filep;
         struct qsmm_cspur_s *cspur_p;  // [nspur]
         struct qsmm_sspur_s *sspur_p;  // [nspur]
     };
     
     
     static int save_acstate(qsmm_storage_t storage,
                             int ngram_sz,
                             const qsmm_sig_t *sig_ngram_p,
                             void *paramp) {
         int ii, spur_type, nspur, result=-1;
         qsmm_sig_t sig_next=QSMM_SIG_INVALID;
         FILE *filep;
         struct param_s *param_p=paramp;
         struct qsmm_state_s state;
         nspur=param_p->nspur;
         filep=param_p->filep;
         SAVE(ngram_sz);
         for (ii=0; ii<ngram_sz; ii++) SAVE(sig_ngram_p[ii]);
         if (qsmm_get_storage_state_stats(storage, ngram_sz, sig_ngram_p,
                                          &state, param_p->sspur_p)<0)
             goto Exit;
         SAVE(state);
         for (spur_type=0; spur_type<nspur; spur_type++)
             SAVE(param_p->sspur_p[spur_type]);
         while (1) {
             struct qsmm_cycle_s cycle;
             if (qsmm_get_storage_cycle_next(storage, ngram_sz,
                                             sig_ngram_p, &sig_next)<0)
                 goto Exit;
             SAVE(sig_next);
             if (sig_next==QSMM_SIG_INVALID) break;
             if (qsmm_get_storage_cycle_stats(storage, ngram_sz, sig_ngram_p,
                                              sig_next, &cycle,
                                              param_p->cspur_p)<0)
                 goto Exit;
             SAVE(cycle);
             for (spur_type=0; spur_type<nspur; spur_type++)
                 SAVE(param_p->cspur_p[spur_type]);
         }
         result=1;
     
     Exit:
         if (result<0) param_p->err=result;
         return result;
     }
     
     
     int save_actor_state(qsmm_actor_t actor, FILE *filep) {
         char is_random=qsmm_get_actor_random(actor)?1:0;
         int ii, rc, spur_type, result=-1, nsig=qsmm_get_actor_nsig(actor),
             nspur=qsmm_get_actor_nspur(actor),
             ngram_sz=qsmm_get_actor_ngram_sz(actor),
             auto_spur_type=qsmm_get_actor_auto_spur_type(actor);
         long tmd=qsmm_get_actor_discrete_time(actor);
         double tmc=qsmm_get_actor_continuous_time(actor),
             naction_per_evt=qsmm_get_actor_naction_per_evt(actor),
             nsig_ctrl=qsmm_get_actor_nsig_ctrl(actor),
             ktemperature=qsmm_get_actor_ktemperature(actor);
         qsmm_sig_t sig_next, *sig_ngram_p=qsmm_get_actor_sig_ngram(actor);
         struct param_s param;
         const struct qsmm_pair_sig_s *range_sig_p=qsmm_get_actor_range_sig(actor);
         memset(&param,0,sizeof(param));
         if (qsmm_get_actor_large_model(actor)) ERRRET(-3);
         if (auto_spur_type==QSMM_ERR_NOTFOUND) auto_spur_type=-1;
         SAVE(is_random);
         SAVE(nsig);
         SAVE(nspur);
         SAVE(ngram_sz);
         SAVE(auto_spur_type);
         SAVE(tmd);
         SAVE(tmc);
         SAVE(naction_per_evt);
         SAVE(nsig_ctrl);
         SAVE(ktemperature);
         for (ii=0; ii<ngram_sz; ii++) {
             const struct qsmm_pair_sig_s *pair_p=range_sig_p+ii;
             SAVE(pair_p->first);
             SAVE(pair_p->second);
         }
         for (spur_type=0; spur_type<nspur; spur_type++) {
             double spur=0, spur_weight=0;
             enum qsmm_spur_perception_e spur_perception=QSMM_SPUR_PERCEPTION_NORMAL;
             enum qsmm_time_e spur_time=QSMM_TIME_CONTINUOUS;
             rc=qsmm_get_actor_spur(actor,spur_type,&spur);
             assert(rc>=0);
             SAVE(spur);
             rc=qsmm_get_actor_spur_weight(actor,spur_type,&spur_weight);
             assert(rc>=0);
             SAVE(spur_weight);
             rc=qsmm_get_actor_spur_perception(actor, spur_type,
                                               &spur_perception);
             assert(rc>=0);
             SAVE(spur_perception);
             rc=qsmm_get_actor_spur_time(actor,spur_type,&spur_time);
             assert(rc>=0);
             SAVE(spur_time);
         }
         for (sig_next=0; sig_next<nsig; sig_next++) {
             double weight_sig=0;
             rc=qsmm_get_actor_sig_weight(actor,sig_next,&weight_sig);
             if (rc==QSMM_ERR_INVAL) continue;
             assert(rc>=0);
             SAVE(sig_next);
             SAVE(weight_sig);
         }
         sig_next=QSMM_SIG_INVALID;
         SAVE(sig_next);
         for (ii=0; ii<ngram_sz; ii++) SAVE(sig_ngram_p[ii]);
         param.nspur=nspur;
         param.filep=filep;
         if (!(param.cspur_p=malloc(sizeof(*param.cspur_p)*nspur)) ||
             !(param.sspur_p=malloc(sizeof(*param.sspur_p)*nspur)))
             goto Exit;
         if (qsmm_storage_enum_states(qsmm_get_actor_storage(actor), 0, 0,
                                      &save_acstate, &param)<0) {
             if (param.err<0) ERRRET(param.err);
             goto Exit;
         }
         ii=0;
         SAVE(ii);
         result=0;
     
     Exit:
         if (param.sspur_p) free(param.sspur_p);
         if (param.cspur_p) free(param.cspur_p);
         return result;
     }
     
     
     int load_actor_state(qsmm_actor_t actor, FILE *filep) {
         char is_random=0;
         int ii, rc, spur_type, ngram_allo=1, nsig=0, nspur=0, ngram_sz=0,
             result=-1, auto_spur_type=-1;
         long tmd=0;
         double tmc0, tmc=0, naction_per_evt=0, nsig_ctrl=0, ktemperature=0;
         qsmm_sig_t sig_next, *sig_ngram_p, *sig_ngram_storage_p=0;
         const struct qsmm_pair_sig_s *range_sig_p;
         qsmm_storage_t storage;
         struct qsmm_cspur_s *cspur_p=0;
         struct qsmm_sspur_s *sspur_p=0;
         if (qsmm_get_actor_large_model(actor)) ERRRET(-4);
         LOAD(is_random);
         LOAD(nsig);
         LOAD(nspur);
         LOAD(ngram_sz);
         LOAD(auto_spur_type);
         LOAD(tmd);
         LOAD(tmc);
         LOAD(naction_per_evt);
         LOAD(nsig_ctrl);
         LOAD(ktemperature);
         if (nsig!=qsmm_get_actor_nsig(actor)) ERRRET(-4);
         if (nspur!=qsmm_get_actor_nspur(actor)) ERRRET(-4);
         if (ngram_sz!=qsmm_get_actor_ngram_sz(actor)) ERRRET(-4);
         if (auto_spur_type<-1 || auto_spur_type>=nspur) ERRRET(-3);
         qsmm_set_actor_random(actor,is_random);
         rc=qsmm_set_actor_auto_spur_type(actor,auto_spur_type);
         assert(rc>=0);
         qsmm_set_actor_discrete_time(actor,tmd);
         if (!isfinite(tmc) || tmc<0) ERRRET(-3);
         tmc0=qsmm_get_actor_continuous_time(actor);
         rc=qsmm_actor_time_delta(actor,tmc-tmc0);
         assert(rc>=0);
         if (!isfinite(naction_per_evt) ||
             naction_per_evt<=0 || naction_per_evt>1)
             ERRRET(-3);
         rc=qsmm_set_actor_naction_per_evt(actor,naction_per_evt);
         assert(rc>=0);
         if (!isfinite(nsig_ctrl) || nsig_ctrl<2) ERRRET(-3);
         rc=qsmm_set_actor_nsig_ctrl(actor,nsig_ctrl);
         assert(rc>=0);
         if (!isfinite(ktemperature) || ktemperature<=0) ERRRET(-3);
         rc=qsmm_set_actor_ktemperature(actor,ktemperature);
         assert(rc>=0);
         range_sig_p=qsmm_get_actor_range_sig(actor);
         for (ii=0; ii<ngram_sz; ii++) {
             struct qsmm_pair_sig_s range;
             const struct qsmm_pair_sig_s *pair_p=range_sig_p+ii;
             LOAD(range.first);
             LOAD(range.second);
             if (range.first!=pair_p->first) ERRRET(-4);
             if (range.second!=pair_p->second) ERRRET(-4);
         }
         for (spur_type=0; spur_type<nspur; spur_type++) {
             double spur=0, spur0=0, spur_weight=0;
             enum qsmm_spur_perception_e spur_perception=QSMM_SPUR_PERCEPTION_NORMAL;
             enum qsmm_time_e spur_time=QSMM_TIME_CONTINUOUS;
             LOAD(spur);
             if (!isfinite(spur)) ERRRET(-3);
             rc=qsmm_get_actor_spur(actor,spur_type,&spur0);
             assert(rc>=0);
             rc=qsmm_actor_spur_delta(actor,spur_type,spur-spur0);
             assert(rc>=0);
             LOAD(spur_weight);
             if (!isfinite(spur_weight)) ERRRET(-3);
             rc=qsmm_set_actor_spur_weight(actor,spur_type,spur_weight);
             assert(rc>=0);
             LOAD(spur_perception);
             rc=qsmm_set_actor_spur_perception(actor,spur_type,spur_perception);
             assert(rc>=0);
             LOAD(spur_time);
             rc=qsmm_set_actor_spur_time(actor,spur_type,spur_time);
             assert(rc>=0);
         }
         while (1) {
             double weight_sig=0;
             LOAD(sig_next);
             if (sig_next==QSMM_SIG_INVALID) break;
             LOAD(weight_sig);
             if (!isfinite(weight_sig) || weight_sig<0) ERRRET(-3);
             rc=qsmm_set_actor_sig_weight(actor,sig_next,weight_sig);
             if (rc==QSMM_ERR_INVAL) ERRRET(-4);
             assert(rc>=0);
         }
         sig_ngram_p=qsmm_get_actor_sig_ngram(actor);
         for (ii=0; ii<ngram_sz; ii++) {
             LOAD(sig_next);
             if (sig_next>=nsig) ERRRET(-3);
             sig_ngram_p[ii]=sig_next;
         }
         if (!(sig_ngram_storage_p=
               malloc(ngram_allo*sizeof(*sig_ngram_storage_p))) ||
             !(cspur_p=malloc(nspur*sizeof(*cspur_p))) ||
             !(sspur_p=malloc(nspur*sizeof(*sspur_p))))
             goto Exit;
         storage=qsmm_get_actor_storage(actor);
         while (1) {
             int ngram_storage_sz=0;
             struct qsmm_state_s state;
             LOAD(ngram_storage_sz);
             if (ngram_storage_sz<1) break;
             if (ngram_allo<ngram_storage_sz) {
                 qsmm_sig_t *sig_new_p=
                     realloc(sig_ngram_storage_p,
                             ngram_storage_sz*sizeof(*sig_ngram_storage_p));
                 if (!sig_new_p) goto Exit;
                 sig_ngram_storage_p=sig_new_p;
                 ngram_allo=ngram_storage_sz;
             }
             for (ii=0; ii<ngram_storage_sz; ii++)
                 LOAD(sig_ngram_storage_p[ii]);
             LOAD(state);
             for (spur_type=0; spur_type<nspur; spur_type++)
                 LOAD(sspur_p[spur_type]);
             if (qsmm_set_storage_state_stats(storage, ngram_storage_sz,
                                              sig_ngram_storage_p,
                                              &state, sspur_p)<0)
                 goto Exit;
             while (1) {
                 struct qsmm_cycle_s cycle;
                 LOAD(sig_next);
                 if (sig_next==QSMM_SIG_INVALID) break;
                 LOAD(cycle);
                 for (spur_type=0; spur_type<nspur; spur_type++)
                     LOAD(cspur_p[spur_type]);
                 if (qsmm_set_storage_cycle_stats(storage, ngram_storage_sz,
                                                  sig_ngram_storage_p,
                                                  sig_next, &cycle, cspur_p)<0)
                     goto Exit;
             }
         }
         result=0;
     
     Exit:
         if (sspur_p) free(sspur_p);
         if (cspur_p) free(cspur_p);
         if (sig_ngram_storage_p) free(sig_ngram_storage_p);
         return result;
     }


Next: , Previous: Statistics Storage, Up: Top

4 Multinode Model

Multinode model is the concept of using a single actor or a pair of actors for multiple situations, in which producing optimal actions is necessary. Those situations might relate to different entities that are either components of a system you develop, which choose optimal actions using the QSMM framework, or entities external to the system and which behavior is to be learned. Nodes of multinode model represent the entities. The special case of multinode model is a single-node model.

In multinode models used within the QSMM framework, there can exist not more than one node that possesses control at any moment of time. Possessing control means executing a subroutine associated with a node, using which the node can produce actions, or, in short, executing the node. A node can produce or choose actions either deterministically or stochastically. When using a stochastic physical process, such as interference of individual electrons, to produce random numbers used by the subroutine, the node will choose actions in more or less literal sense. Otherwise, when a pseudorandom number generator is used, the choice of actions will be determined by information the node receives from the environment.

A node can call other nodes. When a node finishes execution, it returns control to a caller node or to the system if the system calls the node. Within the QSMM framework, nodes might also call each other recursively.

Every node of multinode model belongs to a particular class of nodes with common behavior. As of QSMM version 1.16, the only supported type of node classes is instruction class set. Instruction class sets are used to implement automatic synthesis of assembler programs that consist of instructions.


Next: , Up: Multinode Model

4.1 Principle of Operation

Nodes produce actions by means of invocation of assembler instructions, which perform effective work, in an instruction execution environment. A schematic diagram of a multinode model implementation is represented in model. The implementation consists of an environment state identification engine, an instruction emitting engine, and the instruction execution environment.

The environment state identification engine guesses current state of a node specified by a node index received from the instruction execution environment. The node is a component of the instruction execution environment and can represent an entity in the external environment with which a system you develop interacts. In simple cases, when single-node models are used, a single node may correspond to the whole environment external to the system, which is why the engine is called the environment state identification engine. That engine sends guessed current node state to the instruction emitting engine. The latter engine emits an assembler instruction based on a node state received and a node index used when guessing that node state.

The instruction execution environment is set up by the developer according to a problem the multinode model has to solve. The main job of the instruction execution environment is handling invocation of assembler instructions, which identifiers are received from the instruction emitting engine. An invoked assembler instruction may change the value of the spur of a particular type accumulated by both engines and a node index sent to those engines, i.e. switch the node context. The invoked instruction returns an instruction outcome and possibly changes the contents of the segment of look-ahead signals. An identifier of the invoked instruction, the instruction outcome, and the segment of look-ahead signals are sent to the environment state identification engine and take part in guessing the next node state.

Guessing node states and choosing which assembler instructions to invoke are spur-driven processes. That is, for example, the goal of the operation of a multinode model could be to identify states of entities contained in the external environment and to perform assembler instructions to maximize the velocity of increment of spur received along with other input from those entities. The concept of a period of time necessary to perform an assembler instruction is applied to calculate the velocity of increment of spur. When executing an instruction, time tracked by both engines is incremented by that period (this is not shown in model). Nodes of a multinode model share common time and spur, so guessing current node state and choosing which instruction to invoke depend on successfulness in solving a problem by all nodes of the model.

Schematic diagram of model implementation

Figure 4.1: schematic diagram of model implementation

An actor, which output signals biuniquely correspond to node (entity) states, represents the environment state identification engine.

A tuple, which consists of an index of a node state identified by the actor at the previous operation step and of a list of signal identifiers of fixed length, represents an action choice state of the actor. One element of that list conveys information on an instruction invoked at the previous operation step and its outcome returned. The list may also contain the segment of look-ahead signals. When multiple nodes are used, a node identifier is additionally included in the action choice state. env_acstate represents the structure of action choice states of the actor. Current state is not part of action choice state, but is shown, because it is the next signal in the event history after action choice state signals.

Action choice states of environment state identification engine

Figure 4.2: action choice states of environment state identification engine

The instruction emitting engine is implemented as an actor, which action choice state is represented by an index of node state. When multiple nodes are used, a node identifier is also included in the action choice state. opt_acstate represents the structure of action choice states of the actor. An output signal is not a part of action choice state, but is shown, because it is the next signal in the event history after action choice state signals.

Action choice states of instruction emitting engine

Figure 4.3: action choice states of instruction emitting engine

The environment state identification engine normally uses an additional spur type that corresponds to the automatic spur, which is a measure of adequacy of a state model within the engine to a sequence of received signals. See Automatic Spur, for additional information.

A helpful feature of a multinode model is the ability to convert internal data stored in its engines to a representation in the form of an assembler program. One can think of such feature as an automatic synthesis of an assembler program that solves an assigned task.

The important thing, which should be realized by the developer, is that automatic synthesis of the assembler program may work only in a situation when there does exist a program with a steady state model that solves an assigned task. The steady state model is a state model in which states have predictable input from the instruction execution environment. The input is transmitted to the states in the form of node identifiers, instruction identifiers, instruction outcomes, and look-ahead signals.

The following concepts were introduced to make possible automatic synthesis of assembler programs.

Instruction meta-class
Represents an instruction name, without taking into account optional instruction parameters that follow the instruction name after a whitespace character. The name of instruction meta-class is the name of instruction that does not include instruction parameters. Example:
          move

This instruction meta-class might represent an instruction that moves an agent in the environment in specific direction. However, a movement direction is not included in the name of the instruction meta-class.

Instruction class
Represents an instruction name, optionally followed by instruction parameters. Distinct strings, where every string consists of an instruction name and normalized instruction parameters (see Setting the Instruction Parameters String, for a description of normalization rules), correspond to distinct instruction classes. Example:
          move north

This instruction class might represent an instruction that moves an agent in the environment one step in the north direction.

An instruction class has a specific number of instruction outcomes that can be analyzed by the environment state identification engine after instruction invocation when determining a new node state and then making a decision, which instruction to execute next.

For example, instruction ‘move north’ and other movement instructions might return a bitmask of four bits (i.e. an integer number in the range 0 to 15) after performing the move. A bit of the bitmask equal to 0 might indicate that there is an obstacle in a corresponding direction, thus giving a piece of information to the agent about configuration of the environment. Depending on the implementation of the agent, it may be useful to return the special instruction outcome 16 if a movement instruction is invoked, but there is an obstacle in a corresponding direction, which disallows the move.

Instruction class set
Is a node class that represents a set of instruction classes and its properties. For example, instruction class set ‘walker’, which could be used to investigate the environment, might contain the following instruction classes:
          move north
          move east
          move south
          move west

Instruction instance
An instance of instruction class in an assembler program. For example, in assembler program fragment
                  move north
                  joe  14, return_back
                  move north
                  ...
          return_back:
                  move south
                  ...

there are two instances of instruction class ‘move north’.


Next: , Previous: Principle of Operation, Up: Multinode Model

4.2 Creating a Multinode Model

A multinode model is referred to by a model handle.

— Data type: qsmm_t

This is a type for a model handle. It is a pointer, so variables of this type can have zero value. Function qsmm_create allocates a new model handle. Function qsmm_destroy frees an existing model handle. After allocating a model handle, it can be passed to API functions that take argument qsmm_t until the handle is freed.

To create and destroy a multinode model, the following functions can be used.

— Function: int qsmm_create (const struct qsmm_desc_s *desc_p, qsmm_t *model_p)

This function creates a multinode model using parameters specified in *desc_p and stores a newly allocated model handle in *model_p.

The function returns a non-negative value on success or a negative error code on failure in creating a multinode model. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Either argument model_p is 0 or parameters specified in *desc_p are invalid.
QSMM_ERR_NOMEM
There was not enough memory to create a multinode model.

— Function: void qsmm_destroy (qsmm_t model)

This function destroys a multinode model specified by model handle model. After multinode model destruction, the model handle must not be used. If model is 0, then the function will do nothing.

A structure, a pointer to which is an argument of function qsmm_create, is described below.

— Structure: qsmm_desc_s

This structure describes parameters for creating a multinode model. It contains the following fields.

— Field: char use_flat_storage

A flag, which specifies whether flat storage or map storage should be used by a pair of actors that correspond to the multinode model. Is assigned to field use_flat_storage of structure qsmm_actor_desc_s passed to function qsmm_actor_create when creating each actor of the pair. It is permissible to use flat storage when fields is_large_env and is_large_opt of this structure have zero values. If the value of field use_flat_storage is non-zero and it is not permissible to use flat storage, then error QSMM_ERR_INVAL will be reported.

— Field: char dont_use_instr_class_weights

A flag that specifies whether to not to set weights of output signals of the instruction emitting engine equal to weights of instruction classes specified by functions qsmm_set_instr_class_weight, qsmm_set_instr_class_weight_by_name_f, and qsmm_set_instr_meta_class_weight. The absence of necessity to set the weights of output signals makes the multinode model execute faster, especially when a sparse probability profile is specified for the action emission matrix of a node, and the number of instruction classes in the instruction class set of the node is large. If the flag is not 0, then those functions will raise error QSMM_ERR_NOSYS.

— Field: char is_determ_opt

A flag that imposes the following restriction on action emission matrices of all nodes of the multinode model: for every node state an action emission matrix must define deterministic choice of an instruction class emitted in that node state. When loading assembler programs into nodes, states are assumed to begin just before user and mixed type instructions, and there is no need to mark unnamed states by stt instructions. It is permissible to set this field to a non-zero value only when field dont_use_instr_class_weights has a non-zero value and field is_large_opt has zero value. Otherwise, error QSMM_ERR_INVAL will be reported.

— Field: char is_large_env

A flag, which specifies whether a large actor or a small actor should be created for the environment state identification engine. The large actor will use binary Huffman trees. It is permissible to set this field to a non-zero value only when field use_flat_storage has zero value and field compat has value 1. Otherwise, error QSMM_ERR_INVAL will be reported.

— Field: char is_large_opt

A flag, which specifies whether a large actor or a small actor should be created for the instruction emitting engine. The large actor will use binary Huffman trees. It is permissible to set this field to a non-zero value only when fields use_flat_storage and is_determ_opt have zero values, field dont_use_instr_class_weights has a non-zero value, and field compat has value 1. Otherwise, error QSMM_ERR_INVAL will be reported.

— Field: int nspur

The number of spur types used in the multinode model. This number includes the automatic spur, though that spur might not be really used. If the value of field is_determ_opt is zero, then the value of field nspur must be greater than 1. If the value of field is_determ_opt is non-zero, then the value of field nspur must be greater than 0.

— Field: int stack_sz_max

The maximum allowed number of frames in the node call stack. If the actual number of stack frames exceeds the maximum allowed number, then error QSMM_ERR_STACKOVR will be raised. If the maximum allowed number of stack frames is too big, then stack overflow may occur without raising the error. That kind of stack overflow typically causes a segmentation fault. The value of this field must be greater than 0.

— Field: int ngram_env_la_sz

The length of a segment of extra signals in an action choice state of the environment state identification engine that take part in the identification of a new node state. If this number is positive, then assembler programs cannot be loaded into nodes of the model. The value of this field must be non-negative.

— Field: int nsig_ngram_env_la

The number of signals that are allowed to be the elements of a segment of extra signals of action choice state, the length of which is specified in field ngram_env_la_sz. If the length is positive, then the number of signals must also be positive. If the length is zero, then the number of signals must also be zero.

— Field: int profile_pool_env_sz

A non-negative size of the pool of probabilities lists in normal form of the environment state identification engine. Is assigned to field profile_pool_sz of structure qsmm_actor_desc_s passed to function qsmm_actor_create when creating the engine.

— Field: int profile_pool_opt_sz

A non-negative size of the pool of probabilities lists in normal form of the instruction emitting engine. Is assigned to field profile_pool_sz of structure qsmm_actor_desc_s passed to function qsmm_actor_create when creating the engine.

— Field: int compat

The compatibility level of algorithms used by the multinode model. Must be 0 or 1. Is assigned to field compat of structure qsmm_actor_desc_s passed to function qsmm_actor_create when creating a pair of actors that correspond to the multinode model. Setting a value of this field to 1 is recommended for your new programs. This manual does not include outdated details, specific to original algorithms, which will be used when this field has value 0.

— Field: double sparse_fill_max

The maximum fill ratio for vectors that hold relative probabilities of output signals choice, on the basis of which engines of the multinode model decide when to use sparse vectors instead of ordinary vectors. Must be a number between 0 and 1 (inclusive). Is assigned to field sparse_fill_max of structure qsmm_actor_desc_s passed to function qsmm_actor_create when creating the engines.

Note: when field is_large_env and/or field is_large_opt have non-zero values or when loading large assembler programs, which specify sparse probability profiles, into model nodes, forgetting to set field sparse_fill_max to a positive value, e.g. to 0.2, will cause bad model performance.

— Field: qsmm_rng_t rng

The handle of a random number generator to be used by the multinode model. See Random Number Generators, for information on how to create, destroy random number generators, and perform other operations on them. Can be 0, in which case an instance of default random number generator is automatically created for use by the multinode model and destroyed upon multinode model destruction.

— Field: struct qsmm_pair_sig_s * range_sig_env_la_p

Ranges of signal identifiers in a segment of extra signals in an action choice state that take part in the identification of a new node state. Those ranges are used to check the validity of the segment, to reduce the memory footprint of flat storage if it is used by a small actor, which represents the environment state identification engine, and to reduce the number of nodes in the multinode model of a large actor, which represents the environment state identification engine. In future versions of the package, the ranges can be used for other purposes. To reduce the memory footprint of the environment state identification engine, it is recommended to specify the ranges as precisely as possible.

This field must have zero value or be a pointer to an array of ngram_env_la_sz elements. Each array element is a pair (see Creating an Actor, for a description of structure qsmm_pair_sig_s). Field first of the pair defines the minimum value of signal identifier and field second of the pair defines the maximum value of signal identifier. The value of first must be less than or equal to the value of second, and the value of second must be less than a value specified in field nsig_ngram_env_la of this structure. If field range_sig_env_la_p has zero value, then it will be assumed that every segment signal can be in the range 0 to the value of field nsig_ngram_env_la (exclusive).

To improve compatibility with future versions of the library, an instance of structure qsmm_desc_s, a pointer to which is passed to function qsmm_create, should be zeroed using function memset before setting values of structure fields.

Some parameters specified when creating a multinode model can be obtained later using the following functions.

— Function: int qsmm_get_use_instr_class_weights (qsmm_t model)

This function returns a positive value if weights of output signals of an instruction emitting engine that corresponds to multinode model are set equal to weights of instruction classes of a node being executed or returns 0 otherwise. The function returns a positive value if field dont_use_instr_class_weights of structure qsmm_desc_s passed to function qsmm_create has zero value when creating the multinode model. The function returns zero if that field has a non-zero value when creating the multinode model. The function never returns negative values.

— Function: int qsmm_get_determ_opt (qsmm_t model)

This function returns a positive value if action emission matrices of all nodes of multinode model are restricted to defining deterministic choice of action for every node state. The function returns a positive value if field is_determ_opt of structure qsmm_desc_s passed to function qsmm_create has a non-zero value when creating the multinode model. Otherwise, zero value is returned. This function never returns negative values.

— Function: int qsmm_get_nspur (qsmm_t model)

This function returns the number of spur types used in multinode model. It is a number specified in field nspur of structure qsmm_desc_s when creating the multinode model by function qsmm_create. The returned value is always positive.

— Function: int qsmm_get_stack_sz_max (qsmm_t model)

This function returns the maximum allowed number of frames in the node call stack of multinode model. It is a number specified in field stack_sz_max of structure qsmm_desc_s when creating the multinode model by function qsmm_create. The returned value is always positive.

— Function: int qsmm_get_ngram_env_la_sz (qsmm_t model)

This function returns the length of a segment of extra signals in an action choice state of the environment state identification engine of multinode model, which take part in the identification of a new node state. It is a number specified in field ngram_env_la_sz of structure qsmm_desc_s when creating the multinode model by function qsmm_create. The returned value is always non-negative.

— Function: int qsmm_get_nsig_ngram_env_la (qsmm_t model)

This function returns the number of signals, which are allowed to be placed into an action choice state of the environment state identification engine of multinode model at extra positions, and which take part in the identification of a new node state. It is a number specified in field nsig_ngram_env_la of structure qsmm_desc_s when creating the multinode model by function qsmm_create. The returned value is always non-negative.


Next: , Previous: Creating a Multinode Model, Up: Multinode Model

4.3 Instruction Meta-class Definition

An instruction meta-class is specified by its name and an event handler function. The event handler function typically processes initialization of instruction classes derived from the instruction meta-class and invocation of instructions, which belong to those instruction classes, during model execution.


Next: , Up: Instruction Meta-class Definition

4.3.1 Function Declaration

An instruction meta-class should normally be declared or defined using the following macro.

— Macro: QSMM_INSTR_META_CLASS (instr_meta_class_name)

This macro declares a prototype for a function named instr_meta_class_name, which represents an instruction meta-class with the same name and is the event handler function of that instruction meta-class. You can prepend the macro with the static keyword to declare or define the static function. A pointer to the function has type qsmm_instr_meta_class_func_t. The function is declared as having return type int and the following arguments.

— Function argument: qsmm_t qsmm

The handle of a multinode model that contains the instruction meta-class.

— Function argument: int qsmm_evt

The type of an event that should be processed by the event handler function. Can be one of these constants: QSMM_EVT_ENT_INIT, QSMM_EVT_ENT_DONE, QSMM_EVT_INSTR_CLASS_INIT, QSMM_EVT_INSTR_CLASS_DONE, QSMM_EVT_ENGINE_INIT, QSMM_EVT_ENGINE_DONE, QSMM_EVT_ACTIVATE.

— Function argument: int qsmm_node

The identifier of a node being executed. Is a valid identifier for event QSMM_EVT_ACTIVATE. For other events is equal to -1.

— Function argument: void * qsmm_param_p

A user parameter of the event handler function. Is equal to the value of the corresponding argument of function qsmm_reg_instr_meta_class called to register the instruction meta-class.

For example, a prototype for a static function, which represents instruction meta-class ‘move’, could be declared as follows:

     static QSMM_INSTR_META_CLASS(move);

Static function ‘move’ could be defined as follows:

     static QSMM_INSTR_META_CLASS(move) {
         ...
     }
— Data type: qsmm_instr_meta_class_func_t

This is a type for a pointer to the event handler function of instruction meta-class, to which the following declaration corresponds:

          typedef int (*qsmm_instr_meta_class_func_t)(
              qsmm_t qsmm,
              int qsmm_evt,
              int qsmm_node,
              void *qsmm_param_p);

See above, for a description of arguments of the event handler function. To improve compatibility with future versions of the library, avoid declaring the event handler functions with such prototype explicitly, use macro QSMM_INSTR_META_CLASS instead.


Next: , Previous: Instruction Meta-class Function Declaration, Up: Instruction Meta-class Definition

4.3.2 Event Handling

The event handler function of an instruction meta-class can process the types of events, which are represented by macros listed below. The type of event is passed to the event handler function via argument qsmm_evt.

— Macro: QSMM_EVT_ENT_INIT

Instruction meta-class initialization. This event is sent by function qsmm_reg_instr_meta_class called to register the instruction meta-class.

You may put initial assignments to variables and allocation of resources, which are used by the instruction meta-class, in a block of code that handles this event, together with other user code associated with the instruction meta-class.

— Macro: QSMM_EVT_ENT_DONE

Instruction meta-class uninitialization. This event is sent to all registered instruction meta-class event handlers by function qsmm_destroy called to destroy the multinode model.

In a block of code, which handles the event, resources could be freed that were allocated when processing event QSMM_EVT_ENT_INIT.

— Macro: QSMM_EVT_INSTR_CLASS_INIT

Instruction class initialization. This event is sent by function qsmm_reg_instr_class called to register an instruction class which is a subclass of the instruction meta-class. Function qsmm_reg_instr_class is usually called from the event handler function of an instruction class set during processing event QSMM_EVT_ENT_INIT for the instruction class set, and the instruction class is registered as belonging to that instruction class set.

A block of code, which handles the event, typically performs the following operations:

The functions mentioned above are described in detail further on in this section.

— Macro: QSMM_EVT_INSTR_CLASS_DONE

Instruction class uninitialization. This event is sent for registered instruction classes upon uninitialization of the instruction meta-class or an instruction class set that contains them, which is currently performed during destruction of multinode model by function qsmm_destroy.

In a block of code, which handles the event, a binary representation of instruction class parameters (see further on in this subsection) could be uninitialized, and additional resources associated with the instruction class, which were allocated while processing event QSMM_EVT_INSTR_CLASS_INIT, could be freed.

— Macro: QSMM_EVT_ENGINE_INIT

Initialization of the model instance. This event is sent to all registered instruction meta-class event handlers (in lexicographical order of names of instruction meta-classes) by function qsmm_engine_create, called to create the model instance, at the end of execution of that function.

In a block of code, which handles the event, initial assignments to user variables specific to the model instance could be made, and additional resources specific to the model instance could be allocated.

— Macro: QSMM_EVT_ENGINE_DONE

Uninitialization of the model instance. This event is sent by function qsmm_engine_destroy to all registered instruction meta-class event handlers at the beginning of execution of that function in order, which is reverse for order, in which event QSMM_EVT_ENGINE_INIT was sent. Function qsmm_engine_destroy can be called explicitly, but is also called automatically when recreating the model instance by function qsmm_engine_create and when destroying the multinode model by function qsmm_destroy.

In a block of code, which handles the event, additional resources could be freed that were allocated when processing event QSMM_EVT_ENGINE_INIT.

— Macro: QSMM_EVT_ACTIVATE

Instruction invocation. A block of code, which handles the event, performs custom actions associated with the instruction class that change system or environment state. In that block of code, the outcome of the instruction can be set using function qsmm_set_instr_outcome. See Handling Instruction Invocation, for more information.

Upon successful completion, the event handler function must return a non-negative value. Specific non-negative value will have no effect on system operation. On error, the event handler function should return a negative value. Such negative value will cause a model error handler to be invoked if it is set.

An instruction class may have parameters associated with it. Instruction class parameters have two forms: binary and textual. A textual form is derived from a binary form. Instruction class parameters in textual form are printed after an instruction meta-class name and together they make up the whole instruction class name. Instruction class parameters in binary form can be specified when registering the instruction class by function qsmm_reg_instr_class. The purpose of the following function is to fetch binary instruction class parameters to a buffer, which could be a variable or a structure instance, in the event handler function of instruction meta-class.

— Function: int qsmm_get_eh_instr_param (qsmm_t model, int bufsz, void *bufp)

This function copies a binary representation of parameters of an instruction class, associated with an event processed, to buffer bufp of size bufsz. The function can be called from the event handler function of an instruction meta-class of multinode model while processing events QSMM_EVT_INSTR_CLASS_INIT, QSMM_EVT_INSTR_CLASS_DONE, and QSMM_EVT_ACTIVATE.

Size bufsz of the buffer in bytes must be greater than or equal to the size of the binary representation of instruction class parameters specified when registering the instruction class using function qsmm_reg_instr_class. If bufsz is greater than the size of the binary representation of the instruction class parameters, then remaining contents of buffer bufp will be left intact.

On success, the function returns a non-negative number equal to the size in bytes of the binary representation of instruction class parameters copied to buffer bufp. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The function was not called from the event handler function of instruction meta-class while processing event QSMM_EVT_INSTR_CLASS_INIT, QSMM_EVT_INSTR_CLASS_DONE, or QSMM_EVT_ACTIVATE.
QSMM_ERR_INVAL
The value of bufsz is less than the size of the binary representation of instruction class parameters specified when registering the instruction class using function qsmm_reg_instr_class.

Because a binary representation of instruction class parameters is needed when processing several types of events, it is reasonable to fetch the parameters at the beginning of the event handler function when processing those types of events. To prevent raising error QSMM_ERR_UNTIMELY and possible invocation of model error handler because of an inappropriate event type, the following macro can be used to check whether the type of event is appropriate to call function qsmm_get_eh_instr_param.

— Macro: QSMM_HAS_INSTR_CLASS (evt)

This macro is expanded to:

          ((evt)==QSMM_EVT_INSTR_CLASS_INIT ||
           (evt)==QSMM_EVT_INSTR_CLASS_DONE ||
           (evt)==QSMM_EVT_ACTIVATE)

This macro is intended for checking whether an instruction class is associated with type evt of an event processed by the event handler function, and it is safe to call function qsmm_get_eh_instr_param and other functions that use instruction class context.

For example, to fetch a binary representation of instruction class parameters at the beginning of the event handler function of instruction meta-class ‘move’, the instruction meta-class definition may be written as follows:

     static QSMM_INSTR_META_CLASS(move) {
         enum direct_e direct=0;
         if (QSMM_HAS_INSTR_CLASS(qsmm_evt))
             qsmm_get_eh_instr_param(qsmm,sizeof(direct),&direct);
         ...
     }

To get the name of an instruction class set to which an instruction class belongs, a function described below can be called when processing an event by the event handler function of instruction meta-class. The described function can also be used to get the name of an instruction class set when processing an event by the event handler function of the instruction class set.

— Function: int qsmm_get_eh_instr_class_set_name (qsmm_t model, const char **instr_class_set_name_pp)

This function sets *instr_class_set_name_pp to the name of an instruction class set that corresponds to an event processed by an event handler function of multinode model. If instr_class_set_name_pp is 0, then *instr_class_set_name_pp will not be set. If the function is called from the event handler function of instruction meta-class while processing events QSMM_EVT_INSTR_CLASS_INIT, QSMM_EVT_INSTR_CLASS_DONE, and QSMM_EVT_ACTIVATE, then it will retrieve the name of an instruction class set, to which an instruction class associated with the event belongs. If the function is called from the event handler function of instruction class set, then it will retrieve the name of that instruction class set.

On success, the function returns a non-negative value. If the function is not called from the event handler function of instruction meta-class while processing event QSMM_EVT_INSTR_CLASS_INIT, QSMM_EVT_INSTR_CLASS_DONE, or QSMM_EVT_ACTIVATE, and is not called from the event handler function of instruction class set, then negative error code QSMM_ERR_UNTIMELY will be returned.


Next: , Previous: Instruction Meta-class Event Handling, Up: Instruction Meta-class Definition

4.3.3 Registering the Function

An event handler function of instruction meta-class can be registered using the following macro.

— Macro: QSMM_REG_INSTR_META_CLASS (model, instr_meta_class_name, paramp)

This macro registers instruction meta-class instr_meta_class_name for multinode model. The instruction meta-class should be previously defined using macro QSMM_INSTR_META_CLASS. Parameter paramp will be passed as a value of argument qsmm_param_p of the event handler function of the instruction meta-class on processing all types of events.

This macro is expanded to:

          qsmm_reg_instr_meta_class((model), #instr_meta_class_name,
                                    &instr_meta_class_name, (paramp))

A function called by macro QSMM_REG_INSTR_META_CLASS is described below.

— Function: int qsmm_reg_instr_meta_class (qsmm_t model, const char *instr_meta_class_name, qsmm_instr_meta_class_func_t instr_meta_class_func, void *paramp)

This function registers instruction meta-class instr_meta_class_name for multinode model. Function instr_meta_class_func will be used as an event handler function of the instruction meta-class. Parameter paramp will be passed as a value of argument qsmm_param_p of the event handler function on processing all types of events.

A string pointed by instr_meta_class_name must begin with an English letter or an underscore and must be followed by zero or more characters, where each character is an English letter, a decimal digit, or an underscore.

After registering an instruction meta-class, function qsmm_reg_instr_meta_class sends event QSMM_EVT_ENT_INIT to the event handler function of the instruction meta-class, which can perform initialization of the instruction meta-class.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The model instance already exists, so changing model structure is not allowed.
QSMM_ERR_INVAL
A string pointed by instr_meta_class_name has invalid format.
QSMM_ERR_EXIST
An instruction meta-class or instruction class set named instr_meta_class_name is already registered.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To get a pointer to the event handler function of an instruction meta-class, the following function can be used.

— Function: int qsmm_get_instr_meta_class_handler (qsmm_t model, const char *instr_meta_class_name, qsmm_instr_meta_class_func_t *instr_meta_class_func_p, void **param_pp)

This function retrieves parameters, which are associated with the event handler function of instruction meta-class instr_meta_class_name registered for multinode model. If instr_meta_class_func_p is not 0, then *instr_meta_class_func_p will be set to a pointer to the event handler function. If param_pp is not 0, then *param_pp will be set to a value of argument qsmm_param_p of that function when it is called to process an event.

This function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
Instruction meta-class instr_meta_class_name not found.
QSMM_ERR_TYPE
An entity named instr_meta_class_name is not an instruction meta-class. The entity is an instruction class set.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Registering the Instruction Meta-class Function, Up: Instruction Meta-class Definition

4.3.4 Setting the Instruction Parameters String

An instruction class parameters string is a textual representation of binary instruction class parameters. If an instruction class has parameters, then the instruction class parameters string must be set by the event handler function of instruction meta-class while processing event QSMM_EVT_INSTR_CLASS_INIT. Setting the parameters string can be done using the following function.

— Function: int qsmm_set_eh_instr_param_str_f (qsmm_t model, const char *fmt, ...)

This function sets a textual representation of parameters of an instruction class associated with an event processed. The function is to be called from the event handler function of an instruction meta-class of multinode model while processing event QSMM_EVT_INSTR_CLASS_INIT. The textual representation of instruction class parameters is formatted according to argument fmt and subsequent arguments, the meaning of which is the same as in function printf. A formatted textual representation of instruction class parameters is then converted to normal form according to rules described further on in this subsection.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The function was not called from the event handler function of instruction meta-class while processing event QSMM_EVT_INSTR_CLASS_INIT.
QSMM_ERR_INVAL
A textual representation of instruction class parameters has invalid format. See below for a description of the format.
QSMM_ERR_ILSEQ
A textual representation of instruction class parameters cannot be converted to a wide string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

For example, setting a textual representation of parameters of an instruction class, which is a subclass of instruction meta-class ‘move’, could be done using the following block of code:

     const char *ccp=0;
     switch (direct) {
         case DIRECT_NORTH: ccp="north"; break;
         case DIRECT_EAST:  ccp="east";  break;
         case DIRECT_SOUTH: ccp="south"; break;
         case DIRECT_WEST:  ccp="west";  break;
     }
     if (ccp) qsmm_set_eh_instr_param_str_f(qsmm,"%s",ccp);

It is assumed that the contents of direct were obtained using a call to qsmm_get_eh_instr_param at the beginning of the event handler function.

A textual representation of instruction class parameters may contain string literals. String literals must be enclosed in double quotes. Character ‘\’ within a string literal is an escape character that starts an escape sequence. The escape character can be followed by a three-digit octal character code, or by character ‘x’ and by a two-digit hexadecimal character code, or by one of the following characters: ‘a’, ‘b’, ‘f’, ‘n’, ‘r’, ‘t’, ‘v’, ‘'’, ‘"’, ‘\’. The meaning of these escape sequences is the same as in C language.

When setting a textual representation of instruction class parameters using function qsmm_set_eh_instr_param_str_f, an instruction class parameters string is automatically converted to normal form. All instruction classes in an instruction class set, which are subclasses of an instruction meta-class, must have distinct normalized strings of instruction class parameters. The process of normalization consists in the following steps.

  1. A source string is converted to a wide string according to the current locale.
  2. All characters in the wide string starting from the first character L;’ not within a string literal are considered a comment and are discarded.
  3. All whitespace characters not within string literals are discarded.
  4. All string literals are reformatted by converting them to arrays of wide characters and formatting the arrays according to the following rules.
    1. Wide characters L\a’, L\b’, L\f’, L\n’, L\r’, L\t’, L\v’, L"’, L\\’ are replaced with corresponding escape sequences.
    2. Other wide characters with codes less than 32 are replaced with corresponding octal escape sequences.
    3. All other wide characters are copied to the formatted string as is.
    4. The resulting formatted string is enclosed in double quotes.

To get in an event handler function of instruction meta-class a normalized textual representation of instruction class parameters, the following function can be used.

— Function: int qsmm_get_eh_instr_param_str (qsmm_t model, const char **param_str_pp)

This function sets *param_str_pp to a normalized textual representation of parameters of an instruction class associated with an event processed. A pointer returned in *param_str_pp will be valid until the next call to function qsmm_get_eh_instr_param_str or qsmm_get_instr_class_param_str (see Registering Instruction Classes) for that instruction class. If param_str_pp is 0, then *param_str_pp will not be set. The function can be called from the event handler function of an instruction meta-class of multinode model while processing events QSMM_EVT_INSTR_CLASS_INIT, QSMM_EVT_INSTR_CLASS_DONE, and QSMM_EVT_ACTIVATE.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The function was not called from the event handler function of instruction meta-class while processing event QSMM_EVT_INSTR_CLASS_INIT, QSMM_EVT_INSTR_CLASS_DONE, or QSMM_EVT_ACTIVATE.
QSMM_ERR_ILSEQ
A normalized textual representation of instruction class parameters cannot be converted to a multibyte string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Setting the Instruction Parameters String, Up: Instruction Meta-class Definition

4.3.5 Setting the Number of Instruction Outcomes

To an instruction class the number of instruction outcomes corresponds. Instructions that belong to the instruction class can return those outcomes. The outcomes returned may affect further execution of an assembler program.

The number of instruction outcomes must be non-negative. Special value 0 indicates that an instruction may analyze an outcome of the previous instruction invoked, use that outcome as its own outcome, or change it to a non-negative value less than the maximum number of outcomes of instruction classes that belong to the instruction class set. The default number of outcomes of instruction class is equal to 1.

The purpose of the following function is to set the number of instruction outcomes while processing event QSMM_EVT_INSTR_CLASS_INIT by the event handler function of instruction meta-class.

— Function: int qsmm_set_eh_noutcome (qsmm_t model, int noutcome)

This function sets the number of outcomes of an instruction class associated with an event processed. The function is to be called from the event handler function of an instruction meta-class of multinode model while processing event QSMM_EVT_INSTR_CLASS_INIT.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The function was not called from the event handler function of instruction meta-class while processing event QSMM_EVT_INSTR_CLASS_INIT.
QSMM_ERR_INVAL
The value of noutcome is negative.

To get the number of outcomes of an instruction class associated with an event processed by the event handler function of instruction meta-class, the following function can be used.

— Function: int qsmm_get_eh_noutcome (qsmm_t model)

This function returns the number of outcomes of an instruction class associated with an event processed. The function can be called from the event handler function of an instruction meta-class of multinode model while processing events QSMM_EVT_INSTR_CLASS_INIT, QSMM_EVT_INSTR_CLASS_DONE, and QSMM_EVT_ACTIVATE.

On success, a non-negative value is returned. If the function is not called from the event handler function of instruction meta-class while processing event QSMM_EVT_INSTR_CLASS_INIT, QSMM_EVT_INSTR_CLASS_DONE, or QSMM_EVT_ACTIVATE, then negative error code QSMM_ERR_UNTIMELY will be returned.


Previous: Setting the Number of Instruction Outcomes, Up: Instruction Meta-class Definition

4.3.6 Function Layout

Below there is given a template for the event handler function of instruction meta-class.

     QSMM_INSTR_META_CLASS(instr_meta_class_name) {
         struct param_s param;
             // structure "param_s" is used to hold instruction parameters in a
             // binary form
         // TODO: declare (and possibly initialize) other automatic variables.
         if (QSMM_HAS_INSTR_CLASS(qsmm_evt))
             qsmm_get_eh_instr_param(qsmm,sizeof(param),&param);
         switch (qsmm_evt) {
             case QSMM_EVT_ENT_INIT:
                 // TODO: handle the instruction meta-class initialization
                 //       event.
                 break;
             case QSMM_EVT_ENT_DONE:
                 // TODO: handle the instruction meta-class uninitialization
                 //       event.
                 break;
             case QSMM_EVT_INSTR_CLASS_INIT:
                 qsmm_set_eh_instr_param_str_f(
                     qsmm, fmt, param.field, ...);
                     // set a textual representation of binary parameters of
                     // the instruction class
                 qsmm_set_eh_noutcome(qsmm,noutcome);
                     // set the number of outcomes of the instruction class if
                     // it is not equal to 1
                 // TODO: perform other initialization procedures for the
                 //       instruction class.
                 break;
             case QSMM_EVT_INSTR_CLASS_DONE:
                 // TODO: handle the instruction class uninitialization event.
                 break;
             case QSMM_EVT_ENGINE_INIT:
                 // TODO: handle the model instance initialization event.
                 break;
             case QSMM_EVT_ENGINE_DONE:
                 // TODO: handle the model instance uninitialization event.
                 break;
             case QSMM_EVT_ACTIVATE:
                 // TODO: handle instruction invocation.
                 qsmm_set_instr_outcome(qsmm,outcome);
                     // set an instruction outcome if the instruction class has
                     // multiple outcomes
                 break;
         }
         return 0;
     }


Next: , Previous: Instruction Meta-class Definition, Up: Multinode Model

4.4 Instruction Class Set Definition

An instruction class set is specified by its name and an event handler function. The event handler function typically processes initialization of the instruction class set, transferring control to nodes of a node class represented by the instruction class set, and returning control from them.


Next: , Up: Instruction Class Set Definition

4.4.1 Function Declaration

You should normally declare or define an instruction class set using the following macro.

— Macro: QSMM_INSTR_CLASS_SET (instr_class_set_name)

This macro declares a prototype for a function named instr_class_set_name, which represents an instruction class set with the same name and is an event handler function of that instruction class set. The macro can be prepended with the static keyword to declare or define the static function. A pointer to the function has type qsmm_instr_class_set_func_t. The function is declared as having return type int and the following arguments.

— Function argument: qsmm_t qsmm

The handle of a multinode model that contains the instruction class set.

— Function argument: int qsmm_evt

The type of an event that should be processed by the event handler function. Can be one of these constants: QSMM_EVT_ENT_INIT, QSMM_EVT_ENT_DONE, QSMM_EVT_ENGINE_INIT, QSMM_EVT_ENGINE_DONE, QSMM_EVT_NODE_ENTER, QSMM_EVT_NODE_LEAVE.

— Function argument: int qsmm_node

The identifier of a node, to which control has just been transferred, or which returns control. Is a valid identifier for events QSMM_EVT_NODE_ENTER and QSMM_EVT_NODE_LEAVE. For other events is equal to -1.

— Function argument: void * qsmm_param_p

A user parameter of the event handler function. For events QSMM_EVT_ENT_INIT, QSMM_EVT_ENT_DONE, QSMM_EVT_ENGINE_INIT, and QSMM_EVT_ENGINE_DONE is equal to the value of the corresponding argument of function qsmm_reg_instr_class_set called to register the instruction class set. For events QSMM_EVT_NODE_ENTER and QSMM_EVT_NODE_LEAVE is equal to the value of the corresponding argument of function qsmm_node_call_default using which the node was called.

For example, a prototype for a static function, which represents instruction class set ‘walker’, could be declared as follows:

     static QSMM_INSTR_CLASS_SET(walker);

Static function ‘walker’ could be defined as follows:

     static QSMM_INSTR_CLASS_SET(walker) {
         ...
     }
— Data type: qsmm_instr_class_set_func_t

This is a type for a pointer to the event handler function of instruction class set, to which the following declaration corresponds:

          typedef int (*qsmm_instr_class_set_func_t)(
              qsmm_t qsmm,
              int qsmm_evt,
              int qsmm_node,
              void *qsmm_param_p);

See above, for a description of arguments of the event handler function. To improve compatibility with future versions of the library, avoid declaring the event handler functions with such prototype explicitly, use macro QSMM_INSTR_CLASS_SET instead.


Next: , Previous: Instruction Class Set Function Declaration, Up: Instruction Class Set Definition

4.4.2 Event Handling

The event handler function of an instruction class set can process the types of events, which are represented by macros listed below. The type of event is passed to the event handler function via argument qsmm_evt.

— Macro: QSMM_EVT_ENT_INIT

Instruction class set initialization. Function qsmm_reg_instr_class_set called to register the instruction class set sends this event.

A block of code, which handles the event, typically performs a subset of the following operations:

See Creating Nodes, for a description of macro QSMM_NODE_CREATE and function qsmm_set_node_nstate.

— Macro: QSMM_EVT_ENT_DONE

Instruction class set uninitialization. This event is sent to all registered event handlers of instruction class sets by function qsmm_destroy called to destroy the multinode model.

In a block of code, which handles the event, additional resources could be freed that were allocated when processing event QSMM_EVT_ENT_INIT.

— Macro: QSMM_EVT_ENGINE_INIT

Initialization of the model instance. This event is sent to all registered event handlers of instruction class sets (in lexicographical order of names of instruction class sets) by function qsmm_engine_create, called to create the model instance, at the end of execution of that function.

In a block of code, which handles the event, setting up probability profiles for nodes could be performed, initial assignments to user variables specific to the model instance could be made, and additional resources specific to the model instance could be allocated.

— Macro: QSMM_EVT_ENGINE_DONE

Uninitialization of the model instance. This event is sent by function qsmm_engine_destroy to all registered event handlers of instruction class sets at the beginning of execution of that function in order, which is reverse to order, in which event QSMM_EVT_ENGINE_INIT was sent. Function qsmm_engine_destroy can be called explicitly, but is also called automatically when recreating the model instance by function qsmm_engine_create and when destroying the multinode model by function qsmm_destroy.

In a block of code, which handles the event, additional resources could be freed that were allocated when processing event QSMM_EVT_ENGINE_INIT.

— Macro: QSMM_EVT_NODE_ENTER

Transferring control to a node that belongs to a node class represented by the instruction class set. Function qsmm_node_call_default, which calls a node, sends this event. An identifier of the node is passed to the event handler function via argument qsmm_node, and a user parameter specified when calling the node is passed via argument qsmm_param_p. Before sending this event to the event handler function, a frame in the system stack and a frame in the user stack (if this stack is used) are created.

In a block of code, which handles the event, initialization of the user stack frame could be performed. Fields of the user stack frame could be set in accordance with a value of argument qsmm_param_p.

To conserve memory and speed up program operation when a model contains many nodes, some of which will turn to be never called at all, a probability profile can be loaded into a node while processing this event on the first call to the node.

— Macro: QSMM_EVT_NODE_LEAVE

Returning control from a node that belongs to a node class represented by the instruction class set. Function qsmm_node_call_default, which calls a node, sends this event. An identifier of the node is passed to the event handler function via argument qsmm_node, and a user parameter specified when calling the node is passed via argument qsmm_param_p. The frame in the system stack and the frame in the user stack (if this stack is used) are destroyed after sending this event to the instruction class set.

In a block of code, which handles the event, uninitialization of the user stack frame could be performed. Before the uninitialization, a value pointed by argument qsmm_param_p could be set in accordance with the contents of the user stack frame.

Upon successful completion, the event handler function must return a non-negative value. Specific non-negative value will have no effect on system operation. On error, the event handler function should return a negative value; this will cause a model error handler to be invoked if it is set.

The name of an instruction class set and the name of its event handler function can be not the same in special cases. In the general case, to get the name of an instruction class set while processing an event by the event handler function of the instruction class set, function qsmm_get_eh_instr_class_set_name can be used. That function is described in detail in Instruction Meta-class Event Handling.


Next: , Previous: Instruction Class Set Event Handling, Up: Instruction Class Set Definition

4.4.3 Registering the Function

An event handler function of instruction class set can be registered using the following macro.

— Macro: QSMM_REG_INSTR_CLASS_SET (model, instr_class_set_name, paramp)

This macro registers instruction class set instr_class_set_name for multinode model. The instruction class set should be previously defined using macro QSMM_INSTR_CLASS_SET. Parameter paramp will be passed for a value of argument qsmm_param_p of an event handler function of the instruction class set when the event handler function is called to process events QSMM_EVT_ENT_INIT, QSMM_EVT_ENT_DONE, QSMM_EVT_ENGINE_INIT, and QSMM_EVT_ENGINE_DONE.

This macro is expanded to:

          qsmm_reg_instr_class_set((model), #instr_class_set_name,
                                   &instr_class_set_name, (paramp))

A function called by macro QSMM_REG_INSTR_CLASS_SET is described below.

— Function: int qsmm_reg_instr_class_set (qsmm_t model, const char *instr_class_set_name, qsmm_instr_class_set_func_t instr_class_set_func, void *paramp)

This function registers instruction class set instr_class_set_name for multinode model model. Function instr_class_set_func will be used as an event handler function of the instruction class set. Parameter paramp will be passed for a value of argument qsmm_param_p of the event handler function on processing events QSMM_EVT_ENT_INIT, QSMM_EVT_ENT_DONE, QSMM_EVT_ENGINE_INIT, and QSMM_EVT_ENGINE_DONE.

After registering an instruction class set, function qsmm_reg_instr_class_set sends event QSMM_EVT_ENT_INIT to the event handler function of the instruction class set, which can perform initialization of the instruction class set.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The model instance already exists, so changing model structure is not allowed.
QSMM_ERR_EXIST
An instruction class set or an instruction meta-class named instr_class_set_name is already registered.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To get a pointer to the event handler function of an instruction class set, the following function can be used.

— Function: int qsmm_get_instr_class_set_handler (qsmm_t model, const char *instr_class_set_name, qsmm_instr_class_set_func_t *instr_class_set_func_p, void **param_pp)

This function retrieves parameters associated with the event handler function of instruction class set instr_class_set_name registered for multinode model. If instr_class_set_func_p is not 0, then *instr_class_set_func_p will be set to a pointer to the event handler function. If param_pp is not 0, then *param_pp will be set to a value of argument qsmm_param_p of that function when it is called to process events QSMM_EVT_ENT_INIT, QSMM_EVT_ENT_DONE, QSMM_EVT_ENGINE_INIT, and QSMM_EVT_ENGINE_DONE.

This function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name not found.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Registering the Instruction Class Set Function, Up: Instruction Class Set Definition

4.4.4 Registering Instruction Classes

Every instruction class is an entity related to two other entities: an instruction meta-class and an instruction class set. An instruction class is a subclass of an instruction meta-class and is an element of an instruction class set. Identical subclasses of an instruction meta-class can be the elements of several instruction class sets.

To register an instruction class, the following function can be used.

— Function: int qsmm_reg_instr_class (qsmm_t model, const char *instr_class_set_name, const char *instr_meta_class_name, int instr_param_sz, const void *instr_param_p)

This function registers an instruction class of multinode model. The instruction class is registered as being a subclass of instruction meta-class instr_meta_class_name and an element of instruction class set instr_class_set_name. Arguments instr_param_p and instr_param_sz specify the contents and the size in bytes of a buffer with a binary representation of instruction class parameters. If instr_param_sz is 0, then instr_param_p can be 0.

When registering the instruction class, function qsmm_reg_instr_class sends event QSMM_EVT_INSTR_CLASS_INIT to the event handler function of the instruction meta-class, which might set a textual representation of instruction class parameters using function qsmm_set_eh_instr_param_str_f and the number of instruction outcomes using function qsmm_set_eh_noutcome.

On success, the function returns a non-negative number that uniquely identifies the registered instruction class in the instruction class set. On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The model instance already exists, so changing model structure is not allowed.
QSMM_ERR_NOTFOUND
Instruction meta-class named instr_meta_class_name or instruction class set named instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_meta_class_name is not an instruction meta-class, or an entity named instr_class_set_name is not an instruction class set.
QSMM_ERR_EXIST
An instruction class with the same binary or textual representation of parameters is already registered for pair <instr_meta_class_name, instr_class_set_name>. The binary representation is passed via arguments instr_param_p and instr_param_sz. The textual representation might be set in the event handler function of instruction meta-class instr_meta_class_name while processing event QSMM_EVT_INSTR_CLASS_INIT, which is sent by function qsmm_reg_instr_class.
QSMM_ERR_VIOLNODE
There exist nodes that belong to a node class represented by instruction class set instr_class_set_name. Therefore, changing the instruction class set is not allowed.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To register an instruction class, which belongs to an instruction class set, in the event handler function of the instruction class set during processing event QSMM_EVT_ENT_INIT, the following macros can be used.

— Macro: QSMM_REG_INSTR_CLASS (instr_meta_class_name)

This macro registers an instruction class which is a subclass of instruction meta-class instr_meta_class_name and which has no binary parameters.

The macro is expanded to:

          qsmm_reg_instr_class((qsmm), __FUNCTION__,
                               #instr_meta_class_name, 0, 0)
— Macro: QSMM_REG_INSTR_CLASS_PARAM (instr_meta_class_name, instr_param)

This macro registers an instruction class which is a subclass of instruction meta-class instr_meta_class_name and which has binary parameters specified in variable instr_param.

The macro is expanded to:

          qsmm_reg_instr_class((qsmm), __FUNCTION__, #instr_meta_class_name,
                               sizeof(instr_param), &(instr_param))

These macros are to be expanded from the event handler function of an instruction class set. The name of the event handler function must coincide with the name of the instruction class set, and normally it does coincide. There must be variable qsmm, which is a handle of multinode model, defined in a function from which the macros are expanded. Normally, that variable is an argument of the event handler function.

A binary representation of parameters of an instruction class to be registered using macro QSMM_REG_INSTR_CLASS_PARAM must be a variable, not a constant. It is because the size of the binary representation has to be determined, and it is done using the sizeof operator for the variable. If you need to specify a constant for a binary representation of instruction class parameters, then assign that constant to a variable of appropriate type and use that variable as an argument of macro QSMM_REG_INSTR_CLASS_PARAM.

For example, to use element DIRECT_NORTH of enumeration direct_e as a binary parameter of an instruction class which is a subclass of instruction meta-class ‘move’, the following lines of code could be used:

     enum direct_e direct=DIRECT_NORTH;
     QSMM_REG_INSTR_CLASS_PARAM(move,direct);

An instruction class is uniquely identified in an instruction class set by a non-negative number. That number cannot be greater than or equal to the total number of instruction classes contained in the instruction class set. To get the total number of instruction classes in the set, the following function can be used.

— Function: int qsmm_get_instr_class_set_sz (qsmm_t model, const char *instr_class_set_name)

This function returns the number of instruction classes contained in instruction class set instr_class_set_name of multinode model.

On success, a non-negative value is returned. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To get a non-negative number that uniquely identifies an instruction class with a specified name in an instruction class set, the following functions can be used.

— Function: int qsmm_find_instr_class_in_set_f (qsmm_t model, const char *instr_class_set_name, const char *fmt, ...)
— Function: int qsmm_find_instr_class_in_set_fv (qsmm_t model, const char *instr_class_set_name, const char *fmt, va_list ap)

These functions return a number that uniquely identifies an instruction class in instruction class set instr_class_set_name of multinode model. The instruction class is specified by its name which is the name of instruction meta-class followed by an optional textual representation of instruction class parameters after one or more whitespace characters.

Function qsmm_find_instr_class_in_set_f formats the name of instruction class according to argument fmt and subsequent arguments, the meaning of which is the same as in function printf. Function qsmm_find_instr_class_in_set_fv formats the name of instruction class according to arguments fmt and ap, the meaning of which is the same as in function vprintf.

Before searching the instruction class in the set, the formatted name is converted to a canonical form: extra whitespace characters before and after the name of instruction meta-class are removed, and textual representation of instruction class parameters is normalized according to rules described in Setting the Instruction Parameters String.

On success, the functions return a non-negative value. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist or the instruction class not found in the instruction class set.
QSMM_ERR_INVAL
The name of instruction class has invalid format.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_ILSEQ
The name of instruction class cannot be converted to a wide string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To get information about an instruction class specified by a number, which uniquely identifies it in an instruction class set, the following functions can be used.

— Function: int qsmm_get_instr_class_name (qsmm_t model, const char *instr_class_set_name, int instr_class, const char **instr_class_name_pp)

This function sets *instr_class_name_pp to a canonicalized name of instruction class which is the name of instruction meta-class followed by an optional normalized textual representation of instruction class parameters after a whitespace character. If instr_class_name_pp is 0, then *instr_class_name_pp will not be set. A pointer returned in *instr_class_name_pp will be valid until the next call to this function. The instruction class is specified by non-negative number instr_class that uniquely identifies it in instruction class set instr_class_set_name of multinode model.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of instr_class is negative or is greater than or equal to the number of instruction classes in instruction class set instr_class_set_name.
QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_ILSEQ
The canonicalized name of instruction class cannot be converted to a multibyte string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_get_instr_class_meta_name (qsmm_t model, const char *instr_class_set_name, int instr_class, const char **instr_meta_class_name_pp)

This function sets *instr_meta_class_name_pp to the name of instruction meta-class of an instruction class. If instr_meta_class_name_pp is 0, then *instr_meta_class_name_pp will not be set. The instruction class is specified by non-negative number instr_class that uniquely identifies it in instruction class set instr_class_set_name of multinode model.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of instr_class is negative or is greater than or equal to the number of instruction classes in instruction class set instr_class_set_name.
QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_get_instr_class_param_str (qsmm_t model, const char *instr_class_set_name, int instr_class, const char **param_str_pp)

This function sets *param_str_pp to a normalized textual representation of parameters of an instruction class. If param_str_pp is 0, then *param_str_pp will not be set. A pointer returned in *param_str_pp will be valid until the next call to function qsmm_get_instr_class_param_str or qsmm_get_eh_instr_param_str for that instruction class. If param_str_pp is not 0, and the instruction class does not have textual parameters, then *param_str_pp will be set to 0. The instruction class is specified by non-negative number instr_class that uniquely identifies it in instruction class set instr_class_set_name of multinode model.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of instr_class is negative or is greater than or equal to the number of instruction classes in instruction class set instr_class_set_name.
QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_ILSEQ
A normalized textual representation of instruction class parameters cannot be converted to a multibyte string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_get_instr_class_param (qsmm_t model, const char *instr_class_set_name, int instr_class, const void **buf_pp)

This function retrieves a binary representation of parameters of an instruction class. The instruction class is specified by non-negative number instr_class that uniquely identifies it in instruction class set instr_class_set_name of multinode model. If buf_pp is not 0, then *buf_pp will be set to a pointer to a binary representation of parameters of the instruction class. If the instruction class does not have binary parameters (i.e. their size is 0), and buf_pp is not 0, then *buf_pp will be set to 0.

On success, the function returns a non-negative size in bytes of a binary representation of parameters of the instruction class. On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of instr_class is negative or is greater than or equal to the number of instruction classes in instruction class set instr_class_set_name.
QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_get_instr_class_noutcome (qsmm_t model, const char *instr_class_set_name, int instr_class)

This function returns the number of instruction outcomes for an instruction class. The instruction class is specified by non-negative number instr_class that uniquely identifies it in instruction class set instr_class_set_name of multinode model. The number of outcomes equal to 0 has special meaning described in Setting the Number of Instruction Outcomes.

On success, a non-negative value is returned. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of instr_class is negative or is greater than or equal to the number of instruction classes in instruction class set instr_class_set_name.
QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Registering Instruction Classes, Up: Instruction Class Set Definition

4.4.5 Setting the Number of States

Every node of a multinode model has a certain number of states, one of which is current node state. Current state of a node is changed automatically according to the input from the instruction execution environment. The input is conveyed in the form of an identifier of the last invoked instruction, an outcome of that instruction, and the contents of the look-ahead signal segment if the multinode model uses this segment.

A prerequisite for the synthesis of an assembler program of tolerable quality for a node is that the node must have a sufficient number of states. At the same time, the more states a node has the more instructions need to be executed by the node for the synthesis of an assembler program of tolerable quality.

When creating a node, its number of states is inherited from the number of states specified for a node class the node belongs to. The node class is represented by an instruction class set. The number of states specified for a node class is the default number of states for newly created nodes and, at the same time, the maximum allowed number of states of nodes of the node class. After creating a node, its number of states can be changed to a value less than the maximum allowed one.

To retrieve or set the maximum number of states for a node class, which is also the default number of states for newly created nodes that belong to the node class, the following functions can be used.

— Function: int qsmm_get_nstate_max (qsmm_t model, const char *instr_class_set_name)

This function returns the maximum allowed number of states of nodes that belong to a node class of multinode model. The returned number is also the default number of states of newly created nodes that belong to the node class.

The node class is represented by instruction class set instr_class_set_name.

On success, a positive value is returned. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_set_nstate_max (qsmm_t model, const char *instr_class_set_name, int nstate)

This function sets the maximum allowed number of states of nodes, which belong to a node class of multinode model, to nstate. That number will also be the default number of states of newly created nodes that belong to the node class.

The node class is represented by instruction class set instr_class_set_name.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of nstate is less than 2.
QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_VIOLAP
The model instance exists and the value of nstate is greater than the number of states of the environment state identification engine. When creating the model instance, that engine is created as having the number of states equal to the maximum value among maximum numbers of states specified using function qsmm_set_nstate_max for instruction class sets registered for the multinode model.
QSMM_ERR_VIOLNODE
The value of nstate is less than the number of states of one or more already created nodes that belong to a node class represented by instruction class set instr_class_set_name.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

The default number of states for newly registered instruction class sets is equal to 2.


Previous: Setting the Number of States, Up: Instruction Class Set Definition

4.4.6 Function Layout

Below there is given a template for the event handler function of instruction class set.

     static QSMM_INSTR_CLASS_SET(instr_class_set_name) {
         struct stack_frame_s *stack_frame_p=0;
             // structure "stack_frame_s" represents a user stack frame
         // TODO: declare (and possibly initialize) other automatic variables.
         switch (qsmm_evt) {
             case QSMM_EVT_ENT_INIT:
                 // Register instruction classes.
                 QSMM_REG_INSTR_CLASS(meta_class_1);
                 QSMM_REG_INSTR_CLASS(meta_class_2);
                 // ...
                 QSMM_REG_INSTR_CLASS_PARAM(meta_class_a,var_a);
                 QSMM_REG_INSTR_CLASS_PARAM(meta_class_b,var_b);
                 // ...
                 qsmm_set_nstate_max(qsmm,__FUNCTION__,nstate);
                     // set the maximum allowed and the default number of
                     // states for nodes that belong to this node class
                 // Create the nodes.
                 QSMM_NODE_CREATE(node1);
                 QSMM_NODE_CREATE(node2);
                 // ...
                 // TODO: perform other initialization procedures.
                 break;
             case QSMM_EVT_ENT_DONE:
                 // TODO: handle the instruction class set uninitialization
                 //       event.
                 break;
             case QSMM_EVT_ENGINE_INIT:
                 // TODO: handle the model instance initialization event.
                 break;
             case QSMM_EVT_ENGINE_DONE:
                 // TODO: handle the model instance uninitialization event.
                 break;
             case QSMM_EVT_NODE_ENTER:
                 qsmm_get_stack_frame(qsmm,0,(void **) &stack_frame_p);
                 // TODO: initialize *stack_frame_p, possibly according to the
                 //       contents of a structure pointed by qsmm_param_p.
                 break;
             case QSMM_EVT_NODE_LEAVE:
                 qsmm_get_stack_frame(qsmm,0,(void **) &stack_frame_p);
                 // TODO: set the contents of a structure pointed by
                 //       qsmm_param_p according to *stack_frame_p and
                 //       uninitialize *stack_frame_p.
                 break;
         }
         return 0;
     }


Next: , Previous: Instruction Class Set Definition, Up: Multinode Model

4.5 Creating Nodes

To create a node, which belongs to a particular node class represented by an instruction class set, the following function can be used.

— Function: int qsmm_node_create (qsmm_t model, const char *instr_class_set_name, int node)

This function creates a node of multinode model. The node will belong to a node class represented by instruction class set instr_class_set_name. If node is non-negative, then the node will have identifier node.

If node is equal to -1, then the function will try to find an unused identifier for the node. If the model instance does not exist, then an identifier found will be an unused identifier in the range 0 to a value returned by function qsmm_get_nnode inclusive. If the model instance exists, then an identifier found will be an unused identifier in the range 0 to a value returned by function qsmm_get_nnode exclusive.

On success, the function returns a non-negative identifier of created node. On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of node is less than -1 or is greater than QSMM_SIG_MAX.
QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_EXIST
A node with identifier node already exists.
QSMM_ERR_VIOLAP
Creating a node with identifier node will violate parameters of already created model instance. To be able to create nodes with greater identifiers after creating the model instance, call function qsmm_node_reserve before creating the model instance. That function is described further on in this section.
QSMM_ERR_MNODE
The value of node is equal to -1, the model instance exists, but there are no unused node identifiers.
QSMM_ERR_NOMEM
There was not enough memory to create a node.

To create a node from within an event handler function of an instruction class set, where the name of the function is the name of the instruction class set, the following macro can be used.

— Macro: QSMM_NODE_CREATE (node)

This macro is expanded to:

          qsmm_node_create((qsmm), __FUNCTION__, (node))

The macro is intended for creating a model node with identifier node from within an event handler function of an instruction class set that represents a node class to which the created node will belong. The name of the event handler function must coincide with the name of the instruction class set, and normally it does coincide. There must be variable qsmm, which is a handle of the multinode model, defined in a function from which the macro is expanded. Normally, that variable is an argument of the event handler function.

The created nodes are destroyed upon destruction of the whole multinode model, which is performed by function qsmm_destroy. Starting from QSMM version 1.15 there is supported dynamic node destruction using the following function.

— Function: int qsmm_node_destroy (qsmm_t model, int node)

This function destroys a node with identifier node contained in multinode model. That identifier becomes the unused one. All statistics, which might have been collected for the node, is cleared.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_PROFSRCP
A node with identifier node is the source of probability profile for other nodes. See Memory Efficient Cloning a Probability Profile, for more information on this mode.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds or raises QSMM_ERR_NOTFOUND, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds or raises QSMM_ERR_NOTFOUND, then the model instance state will become determinate.

To get the name of the node class of a node, the function described below can be used. This function can also be used to test whether a node with given identifier exists.

— Function: const char * qsmm_get_node_class_name (qsmm_t model, int node)

This function returns the name of an instruction class set that represents the node class of a node with identifier node contained in multinode model. If the node does not exist, then the function will return 0.

To reserve node identifiers to allow creating nodes with greater identifiers after making the model instance, a function described below can be used. Without reserving, after making the model instance, nodes can be created with identifiers not greater than the highest identifier of a node created before making the model instance. The default number of reserved nodes is 1.

— Function: int qsmm_node_reserve (qsmm_t model, int node)

This function ensures that the internal array of nodes of multinode model is capable of holding nodes with identifiers up to node inclusively. If necessary, the array is reallocated. Increasing array length can be performed when the model instance is not yet created. The length of the array is used as a parameter for creating the environment state identification engine and the instruction emitting engine that correspond to the model instance.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of node is negative or is greater than QSMM_SIG_MAX.
QSMM_ERR_UNTIMELY
The length of an internal array, which holds nodes, needs to be increased, but the model instance has already been created.
QSMM_ERR_NOMEM
There was not enough memory to perform reallocation of an internal array that holds nodes.

To get the length of the internal array, the following function can be used.

— Function: int qsmm_get_nnode (qsmm_t model)

This function returns the length of the internal array that holds nodes of multinode model. Not all elements of the array may really contain nodes, some of them can have NULL values. The length of the array can be increased by functions qsmm_node_create and qsmm_node_reserve. A value returned by this function is always positive.

A newly created node will have the number of states equal to the maximum allowed number of states of nodes that belong to a node class represented by an instruction class set specified when creating the node. See Setting the Number of States, for a description of functions qsmm_get_nstate_max and qsmm_set_nstate_max using which that maximum allowed number of states can be retrieved or modified. When the environment state identification engine is represented by a small actor or the model contains multiple nodes, the number of states of the node can later be changed to other value.

To retrieve or set the number of states of a node, the following functions can be used.

— Function: int qsmm_get_node_nstate (qsmm_t model, int node)

This function returns the number of states of a node of multinode model.

On success, a positive value is returned. If node does not exist, then negative error code QSMM_ERR_NOTFOUND will be returned.

— Function: int qsmm_set_node_nstate (qsmm_t model, int node, int nstate)

This function sets the number of states of a node of multinode model to nstate.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of nstate is less than 2 or is greater than the maximum allowed number of states of nodes of a node class a node with identifier node belongs to.
QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_NOSTATE
A node with identifier node has a probability profile loaded, and nstate is less than the minimum number of states a node must have to hold the probability profile.
QSMM_ERR_PROFSRCU
A node with identifier node is a user of a source probability profile provided by another node. See Memory Efficient Cloning a Probability Profile, for more information on this mode.
QSMM_ERR_NOSYS
The following conditions are true at once:
  1. The environment state identification engine is represented by a large actor. This mode is specified using field is_large_env of structure qsmm_desc_s when creating the multinode model.
  2. The model contains a single node with identifier 0. Function qsmm_get_nnode will return 1.

Use function qsmm_set_nstate_max to set the number of states of the node.


Next: , Previous: Creating Nodes, Up: Multinode Model

4.6 Creating the Model Instance

A multinode model can have an instance that represents model parameters specific to a single interaction of a system with an external entity or to a single run of the system against input data. That is, a model represents system structure, and the model instance is a system of that structure created to carry out a particular process of interaction or computation or, in other words, model execution. After finishing the process of model execution, the model instance can be destroyed or recreated to perform a new process of model execution.

Creating the model instance implies creating the environment state identification engine and the instruction emitting engine and also initializing the instruction execution environment. To determine parameters necessary to create the pair of engines, all instruction class sets registered for the model are scanned, and the engines are set up using parameters that comply with every registered instruction class set and every existing node.

New instruction class sets, instruction meta-classes, and instruction classes cannot be registered for a model when its instance exists. New nodes cannot be created if they have identifiers greater than the highest node identifier reserved before making the model instance. The highest node identifier can be reserved explicitly, but is also reserved implicitly when creating a new node with an identifier greater than identifiers of all previously created nodes.

To create the model instance, the following function can be used.

— Function: int qsmm_engine_create (qsmm_t model)

This function creates the instance of multinode model. A model instance corresponds to a particular process of interaction or computation performed using a model of a certain structure. If there exists a model instance already created, then it will be destroyed first.

The function returns a non-negative value on success or a negative error code on failure in creating a model instance. Currently, the following error codes can be returned.

QSMM_ERR_BIGMDL
The multinode model has too many variants of the contents of the look-ahead signal segment or contains too many nodes or an instruction class set with too big maximum number of states or which has too many instruction classes or outcomes of those instruction classes.
QSMM_ERR_NOIC
There was encountered an instruction class set that does not contain instruction classes.
QSMM_ERR_NOMEM
There was not enough memory to create the model instance.

To destroy the model instance, the following function can be used.

— Function: void qsmm_engine_destroy (qsmm_t model)

This function destroys the instance of multinode model. Destruction of the instance implies destruction of the environment state identification engine and the instruction emitting engine and removing all statistics they might have collected. If the model instance does not exist, then the function will do nothing. Destruction of the model instance removes restrictions on changing model structure, which are imposed when creating the instance.

The pair of engines, which correspond to the model instance, is represented by an object called actor pair. An actor pair is referred to by an actor pair handle.

— Data type: qsmm_actpair_t

This is a type for an actor pair handle. It is a pointer, so variables of this type can have zero value. The handle of an actor pair, which corresponds to a model instance, can be obtained using function qsmm_get_actpair (see below). The handle will be valid until the model instance is destroyed.

To get the handle of an actor pair associated with the model instance, the following function can be used.

— Function: qsmm_actpair_t qsmm_get_actpair (qsmm_t model)

This function returns the handle of an actor pair associated with the instance of multinode model. The handle will be valid until the model instance is destroyed. If the model instance does not exist, then the function will return 0.

Handles of actors, which comprise an actor pair and represent the engines, can be obtained using functions described below.

— Function: qsmm_actor_t qsmm_get_actpair_actor_env (qsmm_actpair_t actpair)

This function returns the handle of an actor of actor pair actpair that represents the environment state identification engine. This function never returns 0.

— Function: qsmm_actor_t qsmm_get_actpair_actor_opt (qsmm_actpair_t actpair)

This function returns the handle of an actor of actor pair actpair that represents the instruction emitting engine. This function never returns 0.

For the certainty and full awareness of model operating conditions by the developer, it is recommended to explicitly set all key parameters of both engines just after creating the model instance. If the handle of multinode model is stored in variable qsmm, then to assign to variable actor_env the handle of the environment state identification engine and to assign to variable actor_opt the handle of the instruction emitting engine, the following lines of code could be used:

     qsmm_actor_t actor_env, actor_opt;
     qsmm_actpair_t actpair=qsmm_get_actpair(qsmm);
     actor_env=qsmm_get_actpair_actor_env(actpair);
     actor_opt=qsmm_get_actpair_actor_opt(actpair);

For each of two engines, the following parameters are to be set.

  1. The type of a function that returns a relative probability of output signal choice. This parameter is set using a call to qsmm_set_actor_relprob_type. If necessary, a helper function must be provided using a call to qsmm_set_actor_relprob_helper.
  2. A spur type that corresponds to the automatic spur. This parameter is set using function qsmm_set_actor_auto_spur_type. By default, the environment state identification engine uses automatic spur of type 0, and this can be turned off. Spur of type 0 then can be reused for another purpose.
  3. Ways of perception for all supported spur types. The way of spur perception is set using function qsmm_set_actor_spur_perception. By default, mode QSMM_SPUR_PERCEPTION_NORMAL is used for all spur types.
  4. Weights for all supported spur types. The weights are set using function qsmm_set_actor_spur_weight.
  5. The temperature of an engine. This parameter is set using function qsmm_set_actor_ktemperature.

For a small actor, types of time for all supported spur types are to be set using calls to qsmm_set_actor_spur_time.

If an engine is represented by a large actor, say, referred to by handle actor, then the handle of a small actor associated with the large actor can be obtained by expression

     qsmm_get_actpair_actor_env(
         qsmm_get_actpair(qsmm_get_actor_large_model(actor)))

The following parameters are to be additionally set for that small actor.

  1. A spur type that corresponds to the automatic spur. This parameter is set by a call to qsmm_set_actor_auto_spur_type for an actor, which handle was obtained using the above expression. By default, that actor uses automatic spur of type 0, and this mode can be turned off. Spur of type 0 of the small actor then can be reused for another purpose. To spur of type 0 of the small actor there corresponds spur of type -1 of the large actor.
  2. The way of perception for spur of type 0. This parameter is set by a call to qsmm_set_actor_spur_perception either for an actor, which handle was obtained using the above expression, passing 0 for spur type, or for the large actor, passing -1 for spur type. By default, mode QSMM_SPUR_PERCEPTION_NORMAL is used.
  3. Weight for spur type 0. This parameter is set by a call to qsmm_set_actor_spur_weight either for an actor, which handle was obtained using the above expression, passing 0 for spur type, or for the large actor, passing -1 for spur type.


Next: , Previous: Creating the Model Instance, Up: Multinode Model

4.7 Incrementing Time and Spur

To convey to the instance of multinode model information that a period of continuous time has passed, the following function can be used.

— Function: int qsmm_time_delta (qsmm_t model, double time_delta)

This function increments the values of continuous time associated with the environment state identification engine and the instruction emitting engine that correspond to the instance of multinode model. The values of continuous time are incremented by time_delta.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of time_delta is not finite, or the incremented value of continuous time associated with one or both engines becomes non-finite or negative.
QSMM_ERR_UNTIMELY
The model instance does not exist.

When the environment state identification engine is represented by a small actor, spur types supported by the model have zero-based indices. Spur type 0 of the model normally corresponds to the automatic spur. This spur is used only by the environment state identification engine of the model. Spur type 0 of the environment state identification engine normally corresponds to the automatic spur. The value of the automatic spur ordinarily not changed by a program that uses the model.

When the environment state identification engine is represented by a large actor, the model supports special spur type -1 that corresponds to the automatic spur of a small actor associated with the large actor. The value of this spur ordinarily not changed by a program that uses the model.

Spur types of the model with indices greater than 0 correspond to user spur supplied by a program that uses the model. Spur types of the environment state identification engine with indices greater than 0 correspond to user spur. The instruction emitting engine has the number of supported spur types equal to the number of spur types specified when creating the model minus 1. All supported spur types of that engine correspond to user spur. Spur type i of the instruction emitting engine corresponds to spur type i+1 of the environment state identification engine and the model.

To increment the value of the spur of given type for a model instance, the following function can be used.

— Function: int qsmm_spur_delta (qsmm_t model, int spur_type, double spur_delta)

This function increments by spur_delta the value of the spur of type spur_type of the environment state identification engine and possibly of the instruction emitting engine associated with the instance of multinode model. The value of spur_delta can be negative. If spur_type is equal to 0 or -1, then only the value of the spur of type 0 or -1 the environment state identification engine keeps track of will be incremented. If spur_type is equal to j>0, then the value of the spur of type j of the environment state identification engine will be incremented, and the value of the spur of type j-1 of the instruction emitting engine will be incremented.

Spur type 0 corresponds to the automatic spur of the environment state identification engine, the value of which usually not changed by the application program. If the environment state identification engine is represented by a large actor (which is specified using field is_large_env of structure qsmm_desc_s when creating a multinode model), then spur type -1 will correspond to the automatic spur of a small actor associated with the large actor, the value of which usually not changed by the application program too. The number of spur types supported by a multinode model (excluding that special spur type -1) is returned by function qsmm_get_nspur.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
One of the following conditions is met:
  • the environment state identification engine is represented by a small actor and spur_type is negative;
  • the environment state identification engine is represented by a large actor and spur_type is less than -1;
  • the value of spur_type is greater than or equal to the number of spur types specified when creating the multinode model;
  • the value of spur_delta is not finite;
  • the incremented value of the spur for one or both engines becomes non-finite.

QSMM_ERR_UNTIMELY
The model instance does not exist.


Next: , Previous: Incrementing Time and Spur, Up: Multinode Model

4.8 Transferring Control Between Nodes

Currently, transferring control to a node can only be performed by calling the node. A result of transferring control to the node is node activation (if the node is not already active) and node execution. When the node returns control to the caller, and the node is not called recursively, the node becomes the inactive one. Initially, when all nodes are inactive, activation of one of them is performed by a system that has created the multinode model. If there exist active nodes, then one of them will possess control and can call another node by transferring control to it. When calling a node, current state of the called node is reset to the initial one.

To transfer control to a node, the following function can be used.

— Function: int qsmm_node_call_default (qsmm_t model, int node, void *paramp)

This function transfers control to a node of multinode model, executes the node, and exits when the node returns control to the caller.

Transferring control to a node begins with sending event QSMM_EVT_NODE_ENTER to an instruction class set, which is a node class of the node, and passing parameter paramp to the event handler function. If the node does not have a probability profile specified, the environment state identification engine and/or the instruction emitting engine are represented by large actors, and the model contains multiple nodes, then a default uniform probability profile will be loaded into the node. During the execution of the node, events QSMM_EVT_ACTIVATE are sent to meta-classes of instruction classes, which identifiers were generated by the instruction emitting engine. At the end of execution of the node, before returning control to the caller, event QSMM_EVT_NODE_LEAVE is sent to the instruction class set with passing parameter paramp to the event handler function.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_NOCHOICE
There are no positive weights assigned to instruction classes of an instruction class set which is a node class of node. The weights can be specified using functions qsmm_set_instr_class_weight, qsmm_set_instr_class_weight_by_name_f, and qsmm_set_instr_meta_class_weight. The error will leave the multinode model in indeterminate state.
QSMM_ERR_NOPROF
A default uniform probability profile cannot be generated and loaded into node, because action emission matrices of all nodes are restricted to defining deterministic choice of action for every node state (which is specified using field is_determ_opt of structure qsmm_desc_s when creating the multinode model), but an instruction class set, which is a node class of the node, contains multiple instruction classes. It is necessary to load a probability profile into the node manually.
QSMM_ERR_MPROF
There is no room in the pool of probabilities lists in normal form of a large actor that represents the environment state identification engine or the instruction emitting engine when loading a default uniform probability profile into node. Sizes of the pools are specified in fields profile_pool_env_sz and profile_pool_opt_sz of structure qsmm_desc_s when creating the multinode model.
QSMM_ERR_OUTCOME
During the invocation of instruction, an instruction outcome was not set when it ought to be set, or an invalid instruction outcome was set. This will leave the multinode model in indeterminate state.
QSMM_ERR_STACKOVR
The number of frames in the node call stack will exceed the maximum allowed value. That value is specified in field stack_sz_max of structure qsmm_desc_s when creating the multinode model and is returned by function qsmm_get_stack_sz_max.
QSMM_ERR_UNSUPPLA
A default uniform probability profile cannot be loaded into node because the multinode model has positive length of the look-ahead signal segment. That length is specified in field ngram_env_la_sz of structure qsmm_desc_s when creating the multinode model.
QSMM_ERR_ILSEQ
A textual representation of instruction class parameters cannot be converted to a multibyte string according to the current locale. Such conversion is needed when filling structure qsmm_except_outcome_s that corresponds to error code QSMM_ERR_OUTCOME returned by this function. See Error Handling for a Multinode Model, for more information on that structure. The error will leave the multinode model in indeterminate state.
QSMM_ERR_STORAGE
An Actor API function did return error QSMM_ERR_STORAGE. This could leave the multinode model in indeterminate state.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the multinode model in indeterminate state.

To get the number of times a node was called since the model instance had been created, the following function can be used.

— Function: int qsmm_get_node_fq (qsmm_t model, int node, long *fq_p)

This function sets *fq_p to the number of times a node of multinode model was called since the model instance had been created. If the model instance does not exist, then the function will set *fq_p to 0. If fq_p is 0, then *fq_p will not be set.

On success, the function returns a non-negative value. If a node with identifier node does not exist, then the function will return negative error code QSMM_ERR_NOTFOUND.

To get the number of nested calls to a node, the function described below can be used. The function might be used, e.g. to prevent calling already active nodes if the system does not support recursive node calls.

— Function: int qsmm_get_node_recurs (qsmm_t model, int node)

On successful completion, this function returns the non-negative number of times control has been transferred to node of multinode model and is not yet returned. Value 0 means that the node is inactive. Values greater than 0 mean that the node is active. If the node does not exist, then the function will return negative error code QSMM_ERR_NOTFOUND.

When a multinode model contains at least one active node, we can say the model is being executed. After finishing the process of modeling, e.g. when all input data are processed, model execution should be terminated. That is, all active nodes should return control and become inactive ones (the node call stack should become empty).

There is a flag associated with a multinode model, which is when set to a non-zero value, indicates that the process of modeling should be continued, and when set to zero value, indicates that the process of modeling should be successfully terminated. When creating the model instance, the flag is initialized to a non-zero value. To query or set the value of the flag, the following functions can be used.

— Function: int qsmm_get_continue (qsmm_t model)

This function returns a flag that specifies whether the process of execution of multinode model should be continued or terminated. If the function returns a positive value, then the process of model execution should be continued. If the function returns zero, then the process of model execution should be terminated. If the model instance does not exist, then the function will return negative error code QSMM_ERR_UNTIMELY.

— Function: int qsmm_set_continue (qsmm_t model, int flag)

This function sets a flag that specifies whether the process of execution of multinode model should be continued or terminated. If flag is non-zero, then the process of model execution should be continued. If flag is zero, then the process of model execution should be terminated.

On success, the function returns a non-negative value. If the model instance does not exist, then the function will return negative error code QSMM_ERR_UNTIMELY.

The flag, when has zero value, should cause to return from all nested calls to function qsmm_node_call_default. The flag is automatically checked at the beginning of execution of that function, and if it has zero value, the function exits immediately. The flag is also automatically checked after sending event QSMM_EVT_NODE_ENTER to the instruction class set and after every instruction invocation, and if the flag is zero, function qsmm_node_call_default exits. The flag can be explicitly analyzed using function qsmm_get_continue after a call to qsmm_node_call_default when processing event QSMM_EVT_ACTIVATE by the event handler function of instruction meta-class to perform immediate exit from the event handler function when model execution is terminated.

To set the flag to zero, the following macro can also be used.

— Macro: QSMM_TERMINATE ()

This macro is expanded to:

          qsmm_set_continue((qsmm), 0)

You can use this macro to terminate execution of a multinode model from within an event handler function that has argument qsmm equal to the handle of the multinode model.

For example, this macro can be called in a block of code that handles invocation of an instruction (i.e. where event QSMM_EVT_ACTIVATE is processed by the event handler function of instruction meta-class) to terminate execution of a multinode model when all input data of the model are fetched by the instruction.

To return just from the last call to qsmm_node_call_default, the following function can be used.

— Function: void qsmm_return_to_caller_node (qsmm_t model)

This function sets a flag for multinode model, which causes function qsmm_node_call_default to exit. The flag is reset before triggering event QSMM_EVT_NODE_ENTER for the instruction class set to process transfer of control to the node and before triggering event QSMM_EVT_ACTIVATE for an instruction meta-class to process invocation of instruction by the node. The flag is checked after processing event QSMM_EVT_NODE_ENTER by the event handler function of instruction class set and after processing event QSMM_EVT_ACTIVATE by the event handler function of instruction meta-class. If function qsmm_return_to_caller_node is called during processing instruction invocation, then an instruction outcome, which might be set, will be ignored.


Next: , Previous: Transferring Control Between Nodes, Up: Multinode Model

4.9 Handling Instruction Invocation

During system operation, nodes of multinode model execute instructions. When a node has to invoke an instruction, the node triggers event QSMM_EVT_ACTIVATE of an instruction meta-class the instruction belongs to.

When triggering event QSMM_EVT_ACTIVATE, an identifier of the node is passed to the event handler function of instruction meta-class via argument qsmm_node. The event handler function may fetch a binary representation of parameters of the instruction class using a call to qsmm_get_eh_instr_param. The name of an instruction class set the instruction class belongs to can be obtained using a call to qsmm_get_eh_instr_class_set_name. Number idx that uniquely identifies the instruction class in the instruction class set can be fetched from the current frame of the system stack (see Working with System and User Stacks) using call

     qsmm_get_stack_instr_class(qsmm,0,&idx);

While processing event QSMM_EVT_ACTIVATE, the event handler function of instruction meta-class may call nodes of multinode model using function qsmm_node_call_default.

After performing custom actions associated with an instruction class, which change system or environment state, the event handler function may set an instruction outcome. The instruction outcome affects which state is a new state of the node after instruction invocation.

To get or set an instruction outcome, the following functions can be used.

— Function: void qsmm_get_instr_outcome (qsmm_t model, int *outcome_p)

This function sets *outcome_p to an instruction outcome associated with multinode model. If outcome_p is 0, then *outcome_p will not be set. An instruction outcome associated with the multinode model is the last instruction outcome set using a call to qsmm_set_instr_outcome while processing the instruction invocation event. If function qsmm_set_instr_outcome is not called, and the instruction class has a positive number of outcomes, then the instruction outcome returned will be equal to -1. If function qsmm_set_instr_outcome is not called, and the instruction class has zero number of outcomes, then the instruction outcome returned will be the outcome of the previous instruction invoked by a node since control has been transferred to the node or 0 if there is no such instruction.

— Function: int qsmm_set_instr_outcome (qsmm_t model, int outcome)

This function sets an instruction outcome, associated with multinode model, to outcome. The function performs only a preliminary check on the validity of the outcome. Final checks are performed after a return from the event handler function of instruction meta-class, and if the outcome is invalid, then error QSMM_ERR_OUTCOME will be raised by function qsmm_node_call_default.

On success, the function returns a non-negative value. If outcome is less than -1, then negative error code QSMM_ERR_INVAL will be returned.

The number of instruction outcomes is specified using function qsmm_set_eh_noutcome during instruction class initialization. The default number of instruction outcomes, which will be used when that function is not called, is equal to 1. The number of instruction outcomes equal to 0 has special meaning.

Before calling the event handler function of instruction meta-class to process event QSMM_EVT_ACTIVATE, an instruction outcome associated with the multinode model is set to -1 if the instruction class has a positive number of outcomes. If the instruction class has zero number of outcomes, then an instruction outcome associated with the multinode model will be set to the outcome of the previous instruction invoked by the node since control has been transferred to it or to 0 if there is no such instruction.

After calling the event handler function of instruction meta-class to process event QSMM_EVT_ACTIVATE, if an instruction outcome associated with the multinode model is equal to -1, and the number of outcomes of the instruction class is equal to 1, then outcome 0 will be used when choosing the next node state. Such default behavior allows not to call function qsmm_set_instr_outcome at all for instruction classes that have only one possible instruction outcome.

If after calling the event handler function an instruction outcome associated with the multinode model is equal to -1, and the number of outcomes of the instruction class is not equal to 1, then error QSMM_ERR_OUTCOME will be raised by function qsmm_node_call_default. Such check compels to set an instruction outcome using function qsmm_set_instr_outcome if the number of outcomes of the instruction class is greater than 1.

If the instruction class has zero number of outcomes, then while processing event QSMM_EVT_ACTIVATE by the event handler function of instruction meta-class, function qsmm_get_instr_outcome can be called to analyze the outcome of the previous instruction invoked by the node since control has been transferred to it. That outcome can be left intact or can be changed to a non-negative value less than the maximum number among the numbers of outcomes of instruction classes, which belong to an instruction class set that represents a node class of the node.

In QSMM version 1.16 there are added functions for retrieving a probability of the last state transition performed and a probability of the last instruction invoked in a state. The probabilities have type QSMM_PROB_AGGR and are contained in corresponding cells of the state transition matrix and the action emission matrix of the node. The functions can be called during processing event QSMM_EVT_ACTIVATE by the event handler function of instruction meta-class, e.g. to compute the time when the instruction has to take effect.

— Function: double qsmm_get_prob_goto (qsmm_t model)

This function returns the probability of state transition performed just before invoking the last instruction by a node of multinode model. The probability has type QSMM_PROB_AGGR and is stored in a cell of the state transition matrix of the node. The returned value is always in the range 0 to 1 (inclusive). If nodes of the model did not invoke instructions yet, then the function returns 0.

— Function: double qsmm_get_prob_action (qsmm_t model)

This function returns the probability of the last instruction invoked in a state of a node of multinode model just after performing the state transition. The probability has type QSMM_PROB_AGGR and is stored in a cell of the action emission matrix of the node. The returned value is always in the range 0 to 1 (inclusive). If nodes of the model did not invoke instructions yet, then the function returns 0.


Next: , Previous: Handling Instruction Invocation, Up: Multinode Model

4.10 Setting Look-ahead Signals

Look-ahead signals are the ones, which along with an identifier of instruction class and an instruction outcome are used after instruction invocation to determine a new node state. Maybe “look-ahead” is not a correct adjective chosen to denote those signals, it only indicates that in some situations those signals can convey look-ahead information. For example, when processing a sequence of symbols from left to right, look-ahead symbols can be passed to the multinode model in the form of look-ahead signals. In other situations, those signals can be used to pass even look-back information to the model.

The length of the look-ahead signal segment of a multinode model and ranges of signals, which can be assigned to elements of that segment, are specified using fields ngram_env_la_sz, nsig_ngram_env_la, and range_sig_env_la_p of structure qsmm_desc_s when creating the multinode model by function qsmm_create. Those parameters can be retrieved later using functions qsmm_get_ngram_env_la_sz and qsmm_get_nsig_ngram_env_la called for the multinode model and using function qsmm_get_actor_range_sig called for the environment state identification engine that corresponds to the multinode model.

An important limitation imposed on a model when its look-ahead signal segment has positive length is that assembler programs cannot be loaded into nodes of such model. This limitation is also pertinent to loading default uniform probability profiles into nodes of the model using implicitly generated assembler programs when the model contains multiple nodes and the environment state identification engine and/or the instruction emitting engine are represented by large actors. When the look-ahead signal segment has positive length, such probability profiles cannot be loaded into nodes of the model, and the nodes cannot be called. Therefore, when you need to use a look-ahead signal segment of positive length for a model, one or both engines of which are represented by large actors, use a single-node model that contains only a node with identifier 0.

To get or set signals of the look-ahead signal segment at given positions, the following functions can be used.

— Function: int qsmm_get_la_sig (qsmm_t model, int pos, qsmm_sig_t *sigp)

This function sets *sigp to an element of the look-ahead signal segment of multinode model at position pos. If sigp is 0, then *sigp will not be set.

On success, the function returns a non-negative value. If pos is negative or is greater than or equal to the length of the look-ahead signal segment, then negative error code QSMM_ERR_INVAL will be returned.

— Function: int qsmm_set_la_sig (qsmm_t model, int pos, qsmm_sig_t sig)

This function sets an element of the look-ahead signal segment of multinode model at position pos to sig.

On success, the function returns a non-negative value. If pos is negative or is greater than or equal to the length of the look-ahead signal segment, or sig does not fall into a range of allowed signal identifiers at position pos of the look-ahead signal segment, then negative error code QSMM_ERR_INVAL will be returned.

Functions qsmm_get_la_sig and qsmm_set_la_sig can be called at any point after creating a multinode model. For example, they can be called when processing event QSMM_EVT_NODE_ENTER by the event handler function of instruction class set or when processing event QSMM_EVT_ACTIVATE by the event handler function of instruction meta-class.

If field range_sig_env_la_p of structure qsmm_desc_s has zero value, then function qsmm_create will initialize elements of the look-ahead signal segment to 0. If that field has a non-zero value, then function qsmm_create will initialize elements of the look-ahead signal segment to values of field first of corresponding elements of array range_sig_env_la_p.


Next: , Previous: Setting Look-ahead Signals, Up: Multinode Model

4.11 Setting Instruction Classes Weights

If field dont_use_instr_class_weights of structure qsmm_desc_s passed to function qsmm_create when creating a multinode model has zero value, then nodes of the model may have weights assigned to instruction classes that can be executed by the nodes5. Those weights are multipliers for optimal probabilities of invocation of instruction classes. The multiplied probabilities are renormalized and become probabilities that are actually used when stochastically choosing which instruction class to invoke. In fact, the weights are assigned using function qsmm_set_actor_sig_weight to output signals of the instruction emitting engine. Function qsmm_node_create initializes weights of all instruction classes, which can be executed by the node, to 1.

To retrieve or set the weight of an instruction class, which is specified by a number that uniquely identifies it in an instruction class set, the following functions can be used.

— Function: int qsmm_get_instr_class_weight (qsmm_t model, int node, int instr_class, double *weight_p)

This function retrieves the weight of an instruction class that can be executed by node of multinode model. The instruction class is specified by number instr_class that uniquely identifies it in an instruction class set which is a node class of node. If weight_p is not 0, then *weight_p will be set to the retrieved weight.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_INVAL
The value of instr_class is negative or is greater than or equal to the number of instruction classes in an instruction class set which is a node class of node.
QSMM_ERR_NOSYS
The multinode model does not allow to assign weights to instruction classes.

— Function: int qsmm_set_instr_class_weight (qsmm_t model, int node, int instr_class, double weight)

This function sets to weight the weight of an instruction class that can be executed by node of multinode model. The instruction class is specified by number instr_class that uniquely identifies it in an instruction class set which is a node class of node.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_INVAL
The value of weight is not finite or is negative, or the value of instr_class is negative or is greater than or equal to the number of instruction classes in an instruction class set which is a node class of node.
QSMM_ERR_NOSYS
The multinode model does not allow to assign weights to instruction classes.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To retrieve or set the weight of an instruction class, which is specified by its name that consists of an instruction meta-class name and an optional textual representation of instruction class parameters, the following functions can be used.

— Function: int qsmm_get_instr_class_weight_by_name_f (qsmm_t model, int node, double *weight_p, const char *fmt, ...)

This function retrieves the weight of an instruction class that can be executed by node of multinode model. The instruction class is specified by its name, which is the name of instruction meta-class followed by an optional textual representation of instruction class parameters after one or more whitespace characters. The name of instruction class is formatted according to argument fmt and subsequent arguments, the meaning of which is the same as in function printf. If weight_p is not 0, then *weight_p will be set to the retrieved weight.

Before searching the instruction class in an instruction class set which is a node class of node, the formatted name is converted to a canonical form: extra whitespace characters before and after the name of instruction meta-class are removed, and textual representation of instruction class parameters is normalized according to rules described in Setting the Instruction Parameters String.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist or the instruction class not found in an instruction class set which is a node class of node.
QSMM_ERR_INVAL
The name of instruction class has invalid format.
QSMM_ERR_ILSEQ
The name of instruction class cannot be converted to a wide string according to the current locale.
QSMM_ERR_NOSYS
The multinode model does not allow to assign weights to instruction classes.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_set_instr_class_weight_by_name_f (qsmm_t model, int node, double weight, const char *fmt, ...)

This function sets to weight the weight of an instruction class that can be executed by node of multinode model. The instruction class is specified by its name, which is the name of instruction meta-class followed by an optional textual representation of instruction class parameters after one or more whitespace characters. The name of instruction class is formatted according to argument fmt and subsequent arguments, the meaning of which is the same as in function printf.

Before searching the instruction class in an instruction class set which is a node class of node, the formatted name is converted to a canonical form: extra whitespace characters before and after the name of instruction meta-class are removed, and textual representation of instruction class parameters is normalized according to rules described in Setting the Instruction Parameters String.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist or the instruction class not found in an instruction class set which is a node class of node.
QSMM_ERR_INVAL
The value of weight is not finite or is negative, or the name of instruction class has invalid format.
QSMM_ERR_ILSEQ
The name of instruction class cannot be converted to a wide string according to the current locale.
QSMM_ERR_NOSYS
The multinode model does not allow to assign weights to instruction classes.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

For example, to set the weight of instruction class ‘move north’ to 0 to disable moving an agent in the north direction, use a line of code like this:

     qsmm_set_instr_class_weight_by_name_f(qsmm,node,0,"move north");

To set weights of all instruction classes of a node, which are derived from an instruction meta-class, to the same value, the following function can be used.

— Function: int qsmm_set_instr_meta_class_weight (qsmm_t model, const char *instr_meta_class_name, int node, double weight)

This function sets weights of all instruction classes, which belong to instruction meta-class instr_meta_class_name and which can be executed by node of multinode model, to a value equal to weight divided by the number of those instruction classes.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of weight is not finite or is negative.
QSMM_ERR_NOTFOUND
Instruction meta-class instr_meta_class_name not found, or a node with identifier node does not exist, or no instruction classes derived from instruction meta-class instr_meta_class_name are found in an instruction class set which is a node class of node.
QSMM_ERR_TYPE
An entity named instr_meta_class_name is not an instruction meta-class. The entity is an instruction class set.
QSMM_ERR_NOSYS
The multinode model does not allow to assign weights to instruction classes.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Setting Instruction Classes Weights, Up: Multinode Model

4.12 Working with System and User Stacks

The system stack of called nodes contains information on active nodes, i.e. nodes to which control had been transferred and which did not return it yet. A node can occur in several frames of the stack when the node is called recursively. The number of frames in the stack is limited by a value specified in field stack_sz_max of structure qsmm_desc_s when creating the multinode model, which is returned by function qsmm_get_stack_sz_max. The current number of frames in the stack can be retrieved using the following function.

— Function: int qsmm_get_stack_sz (qsmm_t model)

This function returns the current number of frames in the stack of called nodes of multinode model. The returned value is always non-negative.

To get an identifier of called node stored in the system stack at given depth, the following function can be used.

— Function: int qsmm_get_stack_node (qsmm_t model, int depth)

This function returns a non-negative identifier of called node stored in the system stack of multinode model at specified depth. The value of depth must be non-negative and less than a value returned by function qsmm_get_stack_sz. The value of depth equal to 0 corresponds to current stack frame, the value of depth equal to 1 corresponds to the previous stack frame, and so on.

On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The node call stack is empty.
QSMM_ERR_INVAL
The value of depth is negative or is greater than or equal to the number of frames in the node call stack, which is returned by function qsmm_get_stack_sz.

To every node in the node call stack there corresponds a node state, on the basis of which an instruction was invoked or is to be invoked by the node. The node state can be obtained using the following function.

— Function: int qsmm_get_stack_state (qsmm_t model, int depth)

This function returns a non-negative node state index stored in the system stack of multinode model at specified depth. It is a state determined by the environment state identification engine. If the state is not determined yet, then the returned value will be equal to 0.

The value of depth must be non-negative and less than a value returned by function qsmm_get_stack_sz. The value of depth equal to 0 corresponds to current stack frame, the value of depth equal to 1 corresponds to the previous stack frame, and so on.

On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The node call stack is empty.
QSMM_ERR_INVAL
The value of depth is negative or is greater than or equal to the number of frames in the node call stack, which is returned by function qsmm_get_stack_sz.

To a node there might correspond an instruction class of the last instruction invoked by the node. To fetch that information from the node call stack, the following function can be used.

— Function: int qsmm_get_stack_instr_class (qsmm_t model, int depth, int *idx_p)

This function sets *idx_p to the index of an instruction class of the last instruction invoked by a node. That instruction class index is fetched from a frame of the system stack of multinode model at specified depth. If idx_p is 0, then *idx_p will not be set. If there are no instructions invoked by the node since the corresponding frame in the stack is created, and idx_p is not 0, then *idx_p will be set to -1.

The value of depth must be non-negative and less than a value returned by function qsmm_get_stack_sz. The value of depth equal to 0 corresponds to current stack frame, the value of depth equal to 1 corresponds to the previous stack frame, and so on.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The node call stack is empty.
QSMM_ERR_INVAL
The value of depth is negative or is greater than or equal to the number of frames in the node call stack, which is returned by function qsmm_get_stack_sz.

For the means of obtaining information on an instruction class represented by its index, see Registering Instruction Classes.

Besides the system stack, a multinode model can maintain a user stack, which contains application-specific information associated with called nodes. Ordinarily, that information includes values of variables used by instruction classes, and those values make up the execution context of a node.

A frame of the user stack is typically an instance of a structure or a union defined in an application program. The purpose of functions described below is to query or set the size of the frame. Setting the size of the frame should be performed before creating the model instance by function qsmm_engine_create.

— Function: int qsmm_get_stack_frame_sz (qsmm_t model)

This function returns the size in bytes of user stack frame of multinode model. If the user stack is not used, then 0 will be returned. This function never returns negative values.

— Function: int qsmm_set_stack_frame_sz (qsmm_t model, int sz)

This function sets the size of user stack frame of multinode model to sz bytes. Size equal to 0 indicates that the user stack is not used.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The model instance already exists.
QSMM_ERR_INVAL
The value of sz is negative.

Function qsmm_create initializes the size of user stack frame to 0.

When control is transferred to a node, a new frame in the system stack is created. If the size of user stack frame is greater than 0, then a new frame in the user stack will be also created, and the total number of frames in each stack will be equal to a value returned by function qsmm_get_stack_sz. After creating a new frame in the user stack, the frame is initialized with zero bytes.

Event QSMM_EVT_NODE_ENTER is triggered for an instruction class set which is a node class of the node. The event handler function of the instruction class set may process that event and perform application-specific initialization of a newly created user stack frame. For example, the function may allocate dynamic arrays or copy parameters of calling the node to the frame. The parameters are usually passed via a memory block pointed by argument qsmm_param_p of the event handler function.

When the node returns control, event QSMM_EVT_NODE_LEAVE is triggered for the instruction class set. The contents of the current stack frame, which might have been changed while processing events QSMM_EVT_ACTIVATE by event handler functions of instruction meta-classes, can be used to compute results of node invocation. Those results are usually returned via a memory block pointed by argument qsmm_param_p of the event handler function of instruction class set. If necessary, the event handler function must perform application-specific uninitialization of the frame, e.g. free allocated memory.

To get a pointer to a frame of the user stack, the following function can be used.

— Function: int qsmm_get_stack_frame (qsmm_t model, int depth, void **frame_pp)

This function sets *frame_pp to a pointer to a frame of the user stack of multinode model at specified depth. If frame_pp is 0, then *frame_pp will not be set.

The value of depth must be non-negative and less than a value returned by function qsmm_get_stack_sz. The value of depth equal to 0 corresponds to current stack frame, the value of depth equal to 1 corresponds to the previous stack frame, and so on.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOUSTACK
The user stack is not used. Most likely, a positive size of user stack frame was not set by function qsmm_set_stack_frame_sz before creating the model instance.
QSMM_ERR_UNTIMELY
The user stack is empty.
QSMM_ERR_INVAL
The value of depth is negative or is greater than or equal to the number of frames in the user stack, which is returned by function qsmm_get_stack_sz.

If a user stack frame is stored in structure stack_frame_s, then the size of the frame can be set by this call:

     qsmm_set_stack_frame_sz(qsmm,sizeof(struct stack_frame_s));

To get a pointer to current stack frame, use lines of code like these:

     struct stack_frame_s *stack_frame_p=0;
     qsmm_get_stack_frame(qsmm,0,(void **) &stack_frame_p);


Next: , Previous: Working with System and User Stacks, Up: Multinode Model

4.13 Dumping a State Transition Matrix

The state transition matrix of a node contains transition probabilities of supported types along with other numeric information. Rows of the matrix correspond to quadruples, each consisting of a source state, a user or mixed type instruction invoked in that state, the outcome of that instruction, and the contents of the look-ahead signal segment. Columns of the matrix correspond to target transition states. To dump a state transition matrix to a stream, the following function can be used.

— Function: int qsmm_mat_goto_dump (qsmm_t model, int node, struct qsmm_dump_mat_goto_desc_s *desc_p, FILE *filep)

This function dumps a state transition matrix of node of multinode model to stream filep. If node is equal to -1, then state transition matrices of all nodes of the multinode model will be dumped. Dumping is performed according to parameters specified in *desc_p. If desc_p is 0, then default parameters will be used.

In the current implementation, if desc_p is not 0, then *desc_p will not be modified as a result of the function call. However, in future versions of the package the contents of *desc_p could be modified by the function, e.g. statistics on the dumping process could be written there.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_INVAL
The value of desc_p is not 0, and parameters specified in *desc_p are invalid.
QSMM_ERR_NOTFOUND
The value of node is not -1, and a node with identifier node does not exist.
QSMM_ERR_STORAGE
An Actor API function did return error QSMM_ERR_STORAGE. This could leave the model instance in indeterminate state.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state.

A structure, which specifies parameters of dumping, is described below.

— Structure: qsmm_dump_mat_goto_desc_s

This structure specifies parameters of dumping the state transition matrix of a node. It contains the following fields.

— Field: char do_print_prob[QSMM_PROB_COUNT]

An array that specifies probabilities of which types to dump. Indices of the array are the elements of enumeration qsmm_prob_e (except for the last element) described in Generating an Optimal Action. If an element of the array has a non-zero value, then probabilities of the corresponding type will be dumped. The default is to dump probabilities of all types.

— Field: int indent

Left indent, i.e. the number of spaces to print at the beginning of each line of output. Must be a non-negative value. The default is to use indent 0.

— Field: int prob_prec

The number of digits after the decimal point to print for probabilities. If is a positive number, then use fixed-point notation. If is a negative number, then use exponential notation with the number of digits after the decimal point equal to the absolute value of the field. If is zero, then use exponential notation with 15 digits after the decimal point, which is the default mode.

— Field: long fq_min

The minimum value of field fq of an instance of structure qsmm_cycle_s, which corresponds to the intersection of a row and a column of the matrix. Information on instances with lesser value of field fq, i.e. which have lesser frequency, will not be included in the output. The default is to use value 0 for the minimum frequency.

To improve compatibility with future versions of the library, an instance of structure qsmm_dump_mat_goto_desc_s, a pointer to which is passed to function qsmm_mat_goto_dump, should be zeroed using function memset before setting values of structure fields.

Below there is shown a fragment of a dump. When creating the dump, only element QSMM_PROB_LEARNT of field do_print_prob of structure qsmm_dump_mat_goto_desc_s was set to a non-zero value. To make lines of the example shorter, fractional parts of numbers in exponential notation were truncated, and the numbers were rounded.

* State  83

  A0  |mn| O7 L0 : tmd0=183117, tmc0=6E+4, state_next=45,  spur[0].val0=-4E+5, spur[1].val0=4E+2

    S45  : pl=1.0E+00,  spur[0].ds=-3E+05, spur[1].ds=4E+02,  fq=376, ps_d=148416, ps_c=5E+04
    S79  : pl=4.2E-267, spur[0].ds=-4E+04, spur[1].ds=1E+01,  fq=3,   ps_d=23976,  ps_c=8E+03
    S92  : pl=9.1E-283, spur[0].ds=-2E+04, spur[1].ds=4E+00,  fq=2,   ps_d=9516,   ps_c=3E+03

  A0  |mn| O7 L1 : tmd0=183219, tmc0=6E+4, state_next=120, spur[0].val0=-4E+5, spur[1].val0=4E+2

    S63  : pl=2.2E-245, spur[0].ds=-4E+04, spur[1].ds=1E+01,  fq=5,   ps_d=22740,  ps_c=8E+03
    S87  : pl=2.0E-292, spur[0].ds=-3E+02, spur[1].ds=0E+00,  fq=1,   ps_d=156,    ps_c=5E+01
    S120 : pl=1.0E+00,  spur[0].ds=-3E+05, spur[1].ds=4E+02,  fq=119, ps_d=160185, ps_c=5E+04

Below there is a key for the dump. For information on fields of structures referenced, see Structures for Accessing Storage.

Ai
An instruction invoked by the node in a source transition state. Index i is a number that uniquely identifies the instruction, i.e. the instruction class, in an instruction class set which is a node class of the node. Starting from QSMM version 1.15, the name of the instruction class is dumped after token Ai between characters ‘|’.
Li
Look-ahead signal i that was in the look-ahead signal segment of multinode model when the node was in a source transition state. The number of tokens Li is equal to the length of the look-ahead signal segment of multinode model. Positions of tokens Li are equal to positions of look-ahead signals in the look-ahead signal segment.
Oi
Outcome i of an instruction invoked by the node in a source transition state.
Si
The description of a transition to target state i. Starting from QSMM version 1.15, if the state has a name assigned by an argument of stt assembler instruction, then that name will be dumped after token Si in quotes.
RST
This keyword can replace tokens ‘Ax |name| Oy’ for source transition state 0. The keyword indicates that one of specified transitions to target states will be made when control is just transferred to the node. In such situation the node did not invoke instructions yet, so values x and y are unknown.
fq
The value of field fq of structure qsmm_cycle_s.
pa
The probability of type QSMM_PROB_AGGR.
pf
The probability of type QSMM_PROB_FQ.
pl
The probability of type QSMM_PROB_LEARNT.
pp
The probability of type QSMM_PROB_PROFILE.
ps_c
The value of field period_sum_c of structure qsmm_cycle_s.
ps_d
The value of field period_sum_d of structure qsmm_cycle_s.
spur[i].ds
The value of field delta_sum of structure qsmm_cspur_s for spur type i. Spur type 0 usually corresponds to the automatic spur.
spur[i].val0
The value of field val0 of structure qsmm_sspur_s for spur type i. Spur type 0 usually corresponds to the automatic spur.
State
The index of a source transition state. Starting from QSMM version 1.15, if the state has a name assigned by an argument of stt assembler instruction, then that name will be dumped after the index of the state in quotes.
state_next
The index of target state of the last transition made from a source state. Corresponds to the value of field sig_cycle_next of structure qsmm_state_s. Special value ‘N’ corresponds to value QSMM_SIG_INVALID of that field.
tmc0
The value of field tmc0 of structure qsmm_state_s.
tmd0
The value of field tmd0 of structure qsmm_state_s.

Descriptions only of those transitions to target states are dumped, information on which is held in storage. Omitting transitions, information on which is absent in storage, reduces the length of the output when dumping a sparse state transition matrix.

When not all descriptions of transitions are dumped, either because of a positive value of field fq_min of structure qsmm_dump_mat_goto_desc_s or because information on some transitions is absent in storage, the sum of probabilities (of a specific type) of transitions to target states in a row of the matrix may be less than 1. If field fq_min has zero value, then every transition that was not dumped will have a probability equal to (1-p)/n, where p is the sum of probabilities of transitions, which were dumped, and n is the number of transitions that were not dumped.


Next: , Previous: Dumping a State Transition Matrix, Up: Multinode Model

4.14 Dumping an Action Emission Matrix

The action emission matrix of a node contains probabilities of emitting each action of the node in each state of the node. The actions are instruction classes of instruction class set of the node. To dump an action emission matrix to a stream, the following function can be used.

— Function: int qsmm_mat_action_dump (qsmm_t model, int node, struct qsmm_dump_mat_action_desc_s *desc_p, FILE *filep)

This function dumps the action emission matrix of node of multinode model to stream filep. If node is equal to -1, then action emission matrices of all nodes of the multinode model will be dumped. Dumping is performed according to parameters specified in *desc_p. If desc_p is 0, then default parameters will be used.

In the current implementation, if desc_p is not 0, then *desc_p will not be modified as a result of the function call. However, in future versions of the package the contents of *desc_p could be modified by the function, e.g. statistics on the dumping process could be written there.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_INVAL
The value of desc_p is not 0, and parameters specified in *desc_p are invalid.
QSMM_ERR_NOTFOUND
The value of node is not -1, and a node with identifier node does not exist.
QSMM_ERR_STORAGE
An Actor API function did return error QSMM_ERR_STORAGE. This could leave the model instance in indeterminate state.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state.

A structure, which specifies parameters of dumping, is described below.

— Structure: qsmm_dump_mat_action_desc_s

This structure specifies parameters of dumping the action emission matrix of a node. It contains the following fields.

— Field: char do_print_prob[QSMM_PROB_COUNT]

An array that specifies probabilities of which types to dump. Indices of the array are the elements of enumeration qsmm_prob_e (except for the last element) described in Generating an Optimal Action. If an element of the array has a non-zero value, then probabilities of the corresponding type will be dumped. The default is to dump probabilities of all supported types.

— Field: int indent

Left indent, i.e. the number of spaces to print at the beginning of each line of output. Must be a non-negative value. The default is to use indent 0.

— Field: int prob_prec

The number of digits after the decimal point to print for probabilities. If is a positive number, then use fixed-point notation. If is a negative number, then use exponential notation with the number of digits after the decimal point equal to the absolute value of the field. If is zero, then use exponential notation with 15 digits after the decimal point, which is the default mode.

— Field: long fq_min

The minimum value of field fq of an instance of structure qsmm_cycle_s, which corresponds to the intersection of a row and a column of the matrix. Information on instances with lesser value of field fq, i.e. which have lesser frequency, will not be included in the output. The default is to use value 0 for the minimum frequency.

To improve compatibility with future versions of the library, an instance of structure qsmm_dump_mat_action_desc_s, a pointer to which is passed to function qsmm_mat_action_dump, should be zeroed using function memset before setting values of structure fields.

Below there is shown a fragment of a dump. When creating the dump, only element QSMM_PROB_LEARNT of field do_print_prob of structure qsmm_dump_mat_action_desc_s was set to a non-zero value. To make lines of the example shorter, some digits in fractional parts of numbers in exponential notation were replaced with ellipses.

     * State 1 : tmd0=2711, tmc0=1.205...E+03, action_next=0, spur[0].val0=4.000...E+00
     
       A0 |me| : pl=9.9306...E-01, spur[0].ds=4.0...E+00, fq=43, ps_d=1965, ps_c=8.22...E+02
       A1 |ms| : pl=3.4713...E-03, spur[0].ds=0.0...E+00, fq=49, ps_d=147,  ps_c=9.80...E+01
       A2 |mw| : pl=3.4713...E-03, spur[0].ds=0.0...E+00, fq=36, ps_d=108,  ps_c=7.20...E+01

Below there is a key for the dump. For information on fields of structures referenced, see Structures for Accessing Storage.

Ai
The description of an action with index i for a state. Starting from QSMM version 1.15, the name of an instruction class that corresponds to the action is dumped after token Ai between characters ‘|’.
action_next
The index of the last action emitted in a state. Corresponds to the value of field sig_cycle_next of structure qsmm_state_s. Special value ‘N’ corresponds to value QSMM_SIG_INVALID of that field.
fq
The value of field fq of structure qsmm_cycle_s.
pa
The probability of type QSMM_PROB_AGGR.
pf
The probability of type QSMM_PROB_FQ.
pl
The probability of type QSMM_PROB_LEARNT.
pp
The probability of type QSMM_PROB_PROFILE.
ps_c
The value of field period_sum_c of structure qsmm_cycle_s.
ps_d
The value of field period_sum_d of structure qsmm_cycle_s.
spur[i].ds
The value of field delta_sum of structure qsmm_cspur_s for spur type i.
spur[i].val0
The value of field val0 of structure qsmm_sspur_s for spur type i.
State
The description of a state. Starting from QSMM version 1.15, if the state has a name assigned by an argument of stt assembler instruction, then that name will be dumped after the index of the state in quotes.
tmc0
The value of field tmc0 of structure qsmm_state_s.
tmd0
The value of field tmd0 of structure qsmm_state_s.

Starting from QSMM version 1.14, descriptions only of those actions for an action choice state are dumped, information on which is held in storage. Omitting actions, information on which is absent in storage, reduces the length of the output when dumping a sparse action emission matrix.

When not all descriptions of actions are dumped, either because of a positive value of field fq_min of structure qsmm_dump_mat_action_desc_s or because information on some actions is absent in storage, the sum of probabilities (of a specific type) of actions for an action choice state may be less than 1. If field fq_min has zero value, then every action that was not dumped will have a probability equal to (1-p)/n, where p is the sum of probabilities of actions, which were dumped, and n is the number of actions that were not dumped.


Next: , Previous: Dumping an Action Emission Matrix, Up: Multinode Model

4.15 Controlling Random Behavior

A random number generator used by a multinode model, either supplied when creating the model or, if not supplied, an instance of default random number generator allocated automatically, can be obtained using the following function.

— Function: qsmm_rng_t qsmm_get_rng (qsmm_t model)

This function returns the handle of a random number generator used by the environment state identification engine and the instruction emitting engine of multinode model. This function never returns 0.

The handle of a random number generator returned is typically used to seed the generator after creating the multinode model or the model instance. See Random Number Generators, for information on how to seed a random number generator and perform other operations on it.

A useful approach to test the efficiency of operation of a multinode model developed using the QSMM framework is to compare a measure of efficiency calculated for the model during its normal operation with a measure of efficiency calculated when an optimization mechanism provided by the QSMM framework is switched off. The greater is the difference between those values the more optimization mechanism provided by the QSMM framework increases overall optimality of operation of the model.

To switch the model instance to nonoptimal or normal behavior, the following function can be used.

— Function: int qsmm_set_random (qsmm_t model, int flag)

This function switches the current mode of behavior of the instance of multinode model to a nonoptimal or an optimal (normal) mode. If flag is non-zero, then the current mode will be switched to the nonoptimal one. If flag is zero, then the current mode will be switched to the optimal one. In the nonoptimal mode actors, which represent the environment state identification engine and the instruction emitting engine, use equal probabilities of action choice, to which profile probabilities and action weights are applied.

On success, the function returns a non-negative value. If the model instance does not exist, then negative error code QSMM_ERR_UNTIMELY will be returned.

Function qsmm_engine_create initializes the current mode of behavior of the model instance to the optimal (normal) one.


Next: , Previous: Controlling Random Behavior of a Multinode Model, Up: Multinode Model

4.16 Associating Parameters with a Model

When an application creates a multinode model, user variables of the model can be declared as global (or static global) ones and accessed from event handler functions as normal C variables. However, when an application creates several multinode models, there may arise a need to associate a number of variables with a particular model.

There can be a number of pointers associated with a multinode model. They could point to statically or dynamically allocated memory blocks, possibly instances of structures in which variables specific to the model are stored. The pointers are accessed by indices. The set of indices could be defined using the enum keyword.

To get a pointer associated with a model or associate a pointer with the model, the following functions can be used.

— Function: void * qsmm_get_ptr (qsmm_t model, int ptr_idx)

This function returns a pointer with index ptr_idx associated with multinode model. If a pointer with that index is not associated with the model, or the pointer is NULL, then 0 will be returned.

— Function: int qsmm_set_ptr (qsmm_t model, int ptr_idx, void *ptr_p)

This function associates pointer ptr_p with multinode model. The pointer can be fetched later by index ptr_idx.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of ptr_idx is negative.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

Starting from QSMM version 1.15 there is supported associating pointers not only with the whole multinode model, but also with its particular nodes. To get a pointer associated with a node or associate a pointer with the node, the following functions can be used.

— Function: void * qsmm_get_node_ptr (qsmm_t model, int node, int ptr_idx)

This function returns a pointer with index ptr_idx associated with a node of multinode model. If the node does not exist, or a pointer with that index is not associated with the node, or the pointer is NULL, then 0 will be returned.

— Function: int qsmm_set_node_ptr (qsmm_t model, int node, int ptr_idx, void *ptr_p)

This function associates pointer ptr_p with a node of multinode model. The pointer can be fetched later by index ptr_idx.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of ptr_idx is negative.
QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Associating Parameters with a Model, Up: Multinode Model

4.17 Enumerating Entities

An instruction meta-class and an instruction class set are two special types of entities that can be stored within a multinode model. There are also several other types of entities that can be stored there. The notion of entity was introduced primarily to enable passing an entity type and an entity identifier to an error handler function as parameters of an error occurred. That notion has nothing to do with logical entities represented by nodes of the model.

Entity types are declared using the following enumeration.

— Enumeration: qsmm_ent_e

This is an enumeration for categorizing entities stored within a multinode model. It contains the following elements.

QSMM_ENT_INVALID
Invalid entity type, which is currently used to indicate unknown entity type or a type of non-existent entity.
QSMM_ENT_INSTR_CLASS_SET
An instruction class set. Is identified by name. See Principle of Operation, for more information.
QSMM_ENT_INSTR_META_CLASS
An instruction meta-class. Is identified by name. See Principle of Operation, for more information.
QSMM_ENT_INSTR_CLASS
An instruction class. A human-readable identifier of the instruction class consists of an instruction meta-class name and, optionally, a whitespace character and a string representation of instruction parameters. See Principle of Operation, for more information.
QSMM_ENT_NODE
A node of multinode model. Is identified by a non-negative integer.
QSMM_ENT_VAR_PROB
A controlled or output probability variable. Is identified by name. See Using Probability Variables, for more information.
QSMM_ENT_ARRAY_PROB
[New in QSMM 1.16] An output probabilities array. Is identified by name. See Getting Output Probabilities Arrays, for more information.
QSMM_ENT_NODE_CLASS
A synonym for QSMM_ENT_INSTR_CLASS_SET.

Entities of types QSMM_ENT_INSTR_CLASS_SET and QSMM_ENT_INSTR_META_CLASS share the same namespace and must not have duplicate names. That was made because to each of those entities an event handler function with the same name usually corresponds, and function names must be unique.

The identifier of an entity can be stored in a union that contains fields for holding a string or numeric identifier.

— Union: qsmm_ent_u

This union holds an identifier of an entity of one of the types defined by enumeration qsmm_ent_e. The union contains the following fields.

— Field: char *name

A string identifier of the entity. Is applicable to entities of types QSMM_ENT_INSTR_CLASS_SET, QSMM_ENT_INSTR_META_CLASS, QSMM_ENT_INSTR_CLASS, QSMM_ENT_VAR_PROB, QSMM_ENT_ARRAY_PROB, and QSMM_ENT_NODE_CLASS.

— Field: int id

A numeric identifier of the entity. Is applicable to entities of type QSMM_ENT_NODE.

To enumerate entities of given type stored within a multinode model, the following function can be used.

— Function: int qsmm_enum_ent (qsmm_t model, enum qsmm_ent_e ent_type, qsmm_enum_ent_callback_func_t callback_func, void *paramp)

This function enumerates all entities of type ent_type stored in multinode model. The process of enumeration is a repeated calling callback function callback_func, to which an entity type, an entity identifier, and user parameter paramp are passed. If the callback function returns a positive value, then the process of enumeration will be continued. If the callback function returns zero, then the process of enumeration will be terminated, and function qsmm_enum_ent will report success. If the callback function returns a negative value, then the process of enumeration will be terminated, and function qsmm_enum_ent will report failure.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of ent_type is not QSMM_ENT_INSTR_META_CLASS, QSMM_ENT_INSTR_CLASS_SET, QSMM_ENT_NODE_CLASS, and QSMM_ENT_NODE.
QSMM_ERR_CALLBACK
The callback function did return an error.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

The type of a pointer to a callback function, which is called for every enumerated entity, is described below.

— Data type: qsmm_enum_ent_callback_func_t

This is a type of callback function pointer, to which the following declaration corresponds:

          typedef int (*qsmm_enum_ent_callback_func_t)(
              qsmm_t model,
              enum qsmm_ent_e ent_type,
              const union qsmm_ent_u *ent_p,
              void *paramp);

The callback function is called for every enumerated entity of multinode model. The type of an entity is passed via argument ent_type, and an identifier of the entity is passed via argument ent_p. A user parameter is passed via argument paramp.

The callback function may return a positive value if the process of enumeration should be continued, zero if the process of enumeration should be terminated, or a negative value on error.

To get an entity type by an entity name, the following function can be used.

— Function: enum qsmm_ent_e qsmm_get_ent_type_by_name (qsmm_t model, const char *ent_name)

This function returns the type of an entity named ent_name stored in multinode model. There are supported and can be returned entity types QSMM_ENT_INSTR_CLASS_SET and QSMM_ENT_INSTR_META_CLASS. If the entity does not exist or has another type, then value QSMM_ENT_INVALID will be returned.


Next: , Previous: Enumerating Entities, Up: Multinode Model

4.18 Tracing Model Execution

The QSMM framework provides facilities for tracing events related to a multinode model. Types of events, which are dumped to the trace log, can be specified using a bitmask defined as a subset of the following macros merged by bitwise “or.”

— Macro: QSMM_TRACE_API

Multinode model API calls entry and exit. For every call, the name of an API function, names and values of arguments of the function, and a value returned by the function are dumped.

— Macro: QSMM_TRACE_EVT

The start and the end of processing every model event by event handler functions. Event parameters are also dumped.

— Macro: QSMM_TRACE_CTRL

Calling nodes, returning control from nodes, instruction invocations and outcomes of those invocations.

To get or set a bitmask of types of events, which are dumped to the trace log, the following functions can be used.

— Function: unsigned int qsmm_get_trace_flags (qsmm_t model)

This function returns a bitmask of types of events related to multinode model, which are dumped to the trace log. It is a bitmask set by the last call to function qsmm_set_trace_flags or the default bitmask if that function is not called yet.

— Function: void qsmm_set_trace_flags (qsmm_t model, unsigned int flags)

This function sets to flags the bitmask of types of events related to multinode model, which are dumped to the trace log. The function does not check the correctness of the bitmask.

Function qsmm_create initializes the bitmask of types of events, which are dumped to the trace log, to 0.

After setting the bitmask of event types, the events will not be written to the trace log unless a stream that represents the trace log is specified for the multinode model. To get or set a stream for the trace log, the following functions can be used.

— Function: FILE * qsmm_get_trace_stream (qsmm_t model)

This function returns a stream that represents the trace log of multinode model. If the stream is not set, then the function will return 0.

— Function: void qsmm_set_trace_stream (qsmm_t model, FILE *filep)

This function sets to filep a stream that represents the trace log of multinode model. If filep is 0, then event tracing will be disabled for the multinode model.

To write a custom formatted message to the trace log, the following functions can be used.

— Function: void qsmm_trace_f (qsmm_t model, const char *fmt, ...)
— Function: void qsmm_trace_fv (qsmm_t model, const char *fmt, va_list ap)

These functions write a formatted message to the trace log of multinode model. Character ‘\n’ is appended to the message and the stream buffer is flushed. If the trace log is not set, then the functions will do nothing. The meaning of argument fmt and subsequent arguments of function qsmm_trace_f is the same as in function printf. The meaning of arguments fmt and ap of function qsmm_trace_fv is the same as in function vprintf.


Next: , Previous: Tracing Model Execution, Up: Multinode Model

4.19 Error Handling for a Multinode Model

A multinode model can have an error handler assigned to it. An error handler is a function, which is called in case of error raised by any QSMM API function that takes an argument of type qsmm_t and can return an error code. The default error handler function of multinode model prints information on an error occurred to stderr and calls exit(2). If there is no error handler function assigned to the model or an error handler function assigned to the model does not terminate program execution and returns, then an API function, where the error has occurred, will return corresponding error code.

To get or set an error handler for a multinode model, the following functions can be used.

— Function: void qsmm_get_err_handler (qsmm_t model, qsmm_err_handler_func_t *func_p, void **param_pp)

This function retrieves information on an error handler assigned to multinode model. If func_p is not 0, then *func_p will be set to a pointer to an error handler function assigned to the model or to 0 if there is no such function assigned to the model. If param_pp is not 0, then *param_pp will be set to the user parameter of that error handler function.

— Function: void qsmm_set_err_handler (qsmm_t model, qsmm_err_handler_func_t func, void *paramp)

This function assigns an error handler to multinode model. Argument func specifies an error handler function, and argument paramp specifies a user parameter of that function. If func is 0, then no error handler will be used for the model.

An error handler function may receive extended information for some types of errors occurred.

— Data type: qsmm_err_handler_func_t

This is a type of a pointer to an error handler function. To this type the following declaration corresponds:

          typedef void (*qsmm_err_handler_func_t)(
              qsmm_t model,
              struct qsmm_except_s *except_p,
              void *paramp);

The handle of a multinode model, to which an error pertains to, is passed via argument model. Information on the error is passed via argument except_p. A user parameter, specified when setting the error handler function for the model, is passed via argument paramp.

Below there is a description of a structure that contains information on an error occurred.

— Structure: qsmm_except_s

This structure is used to convey to the error handler function of multinode model information about an error occurred. The structure contains the following fields.

— Field: const char * func_name

The name of a function within the QSMM library that has raised the error.

— Field: int code

An error code that will be returned by the QSMM API function after return from the error handler function.

— Field: union qsmm_except_u ee

Extended information on the error occurred. A list of error codes, for which this field always contains the extended information, is given in the description of union qsmm_except_u.

A union that contains extended error information, which depends on the error code, is described below.

— Union: qsmm_except_u

This union contains fields that correspond to error codes for which extended error information is provided.

— Field: char * noic

Extended information for error code QSMM_ERR_NOIC. A description of the error code is “instruction class set does not contain instruction classes.” This field contains the name of an instruction class set that does not contain instruction classes.

— Field: int callback

Extended information for error code QSMM_ERR_CALLBACK. A description of the error code is “callback function returned an error.” This field contains an error code returned by the callback function.

— Field: int noprof

Extended information for error code QSMM_ERR_NOPROF. A description of the error code is “node has no probability profile specified.” This field contains the identifier of a node that has no probability profile specified.

— Field: int violnode

Extended information for error code QSMM_ERR_VIOLNODE. A description of the error code is “the change violates parameters of an already created node.” This field contains the index of a node, which parameters are violated.

— Field: int mprof

Extended information for error code QSMM_ERR_MPROF. A description of the error code is “no room in the pool of probabilities lists in normal form.” This field contains the maximum possible number of probabilities lists in normal form that can be stored in the pool.

— Field: int mnode

Extended information for error code QSMM_ERR_MNODE. A description of the error code is “too many nodes.” This field contains the maximum allowed number of nodes in the model.

— Field: qsmm_msglist_t prg

Extended information for error code QSMM_ERR_PRG. A description of the error code is “invalid program.” This field contains a message list that hands over information on errors in the program.

— Field: qsmm_storage_t storage

Extended information for error code QSMM_ERR_STORAGE. A description of the error code is “storage failure.” This field contains a reference to existing storage the failure pertains to.

— Field: struct qsmm_except_notfound_s notfound

Extended information for error code QSMM_ERR_NOTFOUND. A description of the error code is “entity not found.”

— Field: struct qsmm_except_type_s type

Extended information for error code QSMM_ERR_TYPE. A description of the error code is “invalid entity type.”

— Field: struct qsmm_except_exist_s exist

Extended information for error code QSMM_ERR_EXIST. A description of the error code is “entity already exists.”

— Field: struct qsmm_except_outcome_s outcome

Extended information for error code QSMM_ERR_OUTCOME. A description of the error code is “invalid instruction outcome.”

— Field: struct qsmm_except_evthndlr_s evthndlr

Extended information for error code QSMM_ERR_EVTHNDLR. A description of the error code is “event handler function returned an error.”

— Field: struct qsmm_except_nostate_s nostate

Extended information for error code QSMM_ERR_NOSTATE. A description of the error code is “not enough node states to hold the probability profile.”

— Field: struct qsmm_except_nosamenc_s nosamenc

Extended information for error code QSMM_ERR_NOSAMENC. A description of the error code is “node classes are not the same.”

— Field: struct qsmm_except_profsrcp_s profsrcp

Extended information for error code QSMM_ERR_PROFSRCP. A description of the error code is “node is the source of probability profile for other nodes.”

— Field: struct qsmm_except_profsrcu_s profsrcu

Extended information for error code QSMM_ERR_PROFSRCU. A description of the error code is “node is a user of a source probability profile provided by another node.”

— Field: struct qsmm_except_psumgt1_s psumgt1

Extended information for error code QSMM_ERR_PSUMGT1. A description of the error code is “the sum of probabilities will exceed 1.”

There exists an API function that dumps the contents of structure qsmm_except_s to a stream in a human-readable form. This function is described below.

— Function: void qsmm_except_dump (qsmm_t model, int indent, const struct qsmm_except_s *except_p, FILE *filep)

This function dumps error information contained in *except_p to stream filep in a human-readable form. To dump parameters of a node associated with the error (if such node exists), the function uses the value of argument model that must be the handle of a multinode model to which the error pertains to. Argument indent, which must be non-negative, specifies left indent, i.e. the number of spaces to print at the beginning of each line of output.

In the rest of this section, there are described structures that are the fields of union qsmm_except_u. In some of those structures, fields of types enum qsmm_ent_e and union qsmm_ent_u are used. See Enumerating Entities, for a description of these types.

— Structure: qsmm_except_notfound_s

This structure provides extended information for error code QSMM_ERR_NOTFOUND. A description of the error code is “entity not found.” The structure contains the following fields.

— Field: enum qsmm_ent_e type

The type of an entity that was not found.

— Field: union qsmm_ent_u ent

The name or the identifier of an entity that was not found.

— Structure: qsmm_except_type_s

This structure provides extended information for error code QSMM_ERR_TYPE. A description of the error code is “invalid entity type.” The structure contains the following fields.

— Field: enum qsmm_ent_e type

An invalid type of entity.

— Field: enum qsmm_ent_e type_required

The required type for the entity.

— Field: union qsmm_ent_u ent

The name or the identifier of an entity that has an invalid type.

— Structure: qsmm_except_exist_s

This structure provides extended information for error code QSMM_ERR_EXIST. A description of the error code is “entity already exists.” The structure contains the following fields.

— Field: enum qsmm_ent_e type

The type of an entity that cannot be created, because an entity with the same name or identifier already exists.

— Field: union qsmm_ent_u ent

The name or the identifier of an entity that cannot be created.

— Structure: qsmm_except_outcome_s

This structure provides extended information for error code QSMM_ERR_OUTCOME. A description of the error code is “invalid instruction outcome.” The structure contains the following fields.

— Field: char * instr_meta_class_name

The name of meta-class of an instruction that has an invalid outcome.

— Field: char * instr_param_str_p

The textual representation of parameters of an instruction that has an invalid outcome or 0 if the instruction has no parameters.

— Field: int node

The identifier of a node that has invoked the instruction.

— Field: int outcome

An instruction outcome which is invalid.

— Structure: qsmm_except_evthndlr_s

This structure provides extended information for error code QSMM_ERR_EVTHNDLR. A description of the error code is “event handler function returned an error.” The structure contains the following fields.

— Field: int rc

A negative return value of the event handler function.

— Field: int evt

The type of an event for which the event handler function was called (one of constants defined by QSMM_EVT_* macros).

— Field: int node

The identifier of a node for which the event handler function was called. If there is no node associated with the event, then the identifier will be equal to -1.

— Field: enum qsmm_ent_e type

The type of an entity, which events are handled by the event handler function.

— Field: union qsmm_ent_u ent

The name of an entity, which events are handled by the event handler function.

— Structure: qsmm_except_nostate_s

This structure provides extended information for error code QSMM_ERR_NOSTATE. A description of the error code is “not enough node states to hold the probability profile.” The structure contains the following fields.

— Field: int node

The identifier of a node that has or would have the number of states less than it is required to hold the probability profile.

— Field: int nstate_required

The minimum number of states the node must have to hold the probability profile.

— Structure: qsmm_except_nosamenc_s

This structure provides extended information for error code QSMM_ERR_NOSAMENC. A description of the error code is “node classes are not the same.” The structure contains the following fields.

— Field: char * node_class_name_1

The name of the first node class (i.e. instruction class set).

— Field: char * node_class_name_2

The name of the second node class (i.e. instruction class set).

— Structure: qsmm_except_profsrcp_s

This structure provides extended information for error code QSMM_ERR_PROFSRCP. A description of the error code is “node is the source of probability profile for other nodes.” The structure contains the following fields.

— Field: int node_provider

The identifier of a node that acts as a source of probability profile for other nodes.

— Field: int n_profile_user

The number of nodes, which are users of the probability profile.

— Structure: qsmm_except_profsrcu_s

This structure provides extended information for error code QSMM_ERR_PROFSRCU. A description of the error code is “node is a user of a source probability profile provided by another node.” The structure contains the following fields.

— Field: int node_provider

The identifier of a node that acts as a source of probability profile.

— Field: int node_user

The identifier of a node, which is a user of the probability profile.

— Structure: qsmm_except_psumgt1_s

This structure provides extended information for error code QSMM_ERR_PSUMGT1. A description of the error code is “the sum of probabilities will exceed 1.” The structure contains the following fields.

— Field: char * var_name

The name of a controlled probability variable, after assignment to which the sum of probabilities of case instructions in a choice instruction block or the sum of elements of a probabilities list used by a casels instruction will exceed 1.

— Field: int node

The identifier of a node that contains a state to which the choice instruction block or the casels instruction corresponds.

— Field: int state

The index of a node state to which the choice instruction block or the casels instruction corresponds.


Previous: Error Handling for a Multinode Model, Up: Multinode Model

4.20 Example of Working in Large-scale Mode

The large-scale mode of working with a multinode model is the one when an environment state identification engine and an instruction emitting engine that correspond to the model are represented by large actors. The use of large actors is controlled by fields is_large_env and is_large_opt of structure qsmm_desc_s passed to function qsmm_create when creating the multinode model.

The large scalability consists in the possibility to support large numbers of signals and states by utilizing relatively slow processing units and memory, which in principle could be distributed ones. When using a default uniform probability profile, the large-scale mode does not require possibly time-consuming initialization of working memory only if a single-node model (special case of multinode model) is used. To speed up program operation, if there is a need to work with many nodes, they can be emulated by creating multiple single-node models.

In the example, an agent has to find the gold in a labyrinth represented in labyr and then exit from the labyrinth. The agent does not know its precise location in the labyrinth. The agent receives only limited amount of information about current location, as when a person walking in a labyrinth sees only some signs that got into his eyes and has to reconstruct parts of the labyrinth in his memory without assistance.

The labyrinth is represented by a set of sites connected in various ways. The agent can move from a site in four possible directions. The moves are performed by instructions ‘move north’, ‘move east’, ‘move south’, and ‘move west’. At specific sites only subsets of those moves are allowed, and when the agent tries to perform a disallowed move, that action will be ignored.

After an attempt to perform a move, the agent receives one of the following items of information:

No other information on the configuration of the labyrinth is received by the agent.

To learn the configuration of the labyrinth, the agent visits the labyrinth the number of times defined by macro NVISIT. A visit is considered finished when the agent moves to the labyrinth exit site.

A test labyrinth

Figure 4.4: a test labyrinth

At the end of its run, the sample program prints information on the last path of visiting the labyrinth. Every element of the path contains either letter ‘x’, which indicates that a move was not performed because of an obstacle, or the index of a site (the agent is unaware of that index) and a combination of letters ‘N’, ‘E’, ‘S’, and ‘W’ that specify whether a move in the corresponding direction is possible from the site. If the site had contained the gold and the gold was taken, then ‘(***)’ is printed after that combination of letters.

After the last visiting path there are printed the total number of visits of the labyrinth for which the gold was found, the total length of all traversed visiting paths, and the average amount of gold found per one successful move in the labyrinth. File mat_goto with a state transition matrix and file mat_action with an action emission matrix are created in the current directory. The matrices are dumped rather slowly, and if you do not need them, you can press <CTRL>-<C> to stop that process prematurely.

A random seed can be specified as a program argument. If the random seed is non-negative, then the agent will operate normally. If the random seed is negative, then the agent will move in the labyrinth completely randomly. You could compare the program output for these two modes of program execution.

The source code of the example is provided in file samples/maze.c in the package distribution and is also given below.

     #include <assert.h>
     #include <stdlib.h>
     #include <string.h>
     
     #include <qsmm/qsmm.h>
     
     #define NVISIT 400
     #define NSTATE 148
     
     
     #define ERREXIT(fmt, ...)                                                 \
         do {                                                                  \
             fprintf(stderr,(fmt), ## __VA_ARGS__);                            \
             fprintf(stderr,"\n");                                             \
             goto Exit;                                                        \
         }                                                                     \
         while (0)
     
     
     enum direct_e {
         DIRECT_NORTH=0,
         DIRECT_EAST =1,
         DIRECT_SOUTH=2,
         DIRECT_WEST =3
     };
     
     
     static int visit, n_gold_found=0, path_len=0;
     
     
     static int opaque_maze(enum direct_e direct,
                            unsigned char *percept_p) {
         static char is_gf=0;
         static int node_curr=-1;
         static int maze[][4]={
             {21,  1, -1, -1},  // 0
             {24,  0, -1, 11},  // 1
             {-1,  9, -1, -1},  // 2
             { 7,  4, -1,  8},  // 3
             {-1, -1, -1,  3},  // 4
             {-1,  6, -1, -1},  // 5
             {-1, 12,  7,  5},  // 6
             {14, -1,  3,  6},  // 7
             {10, -1,  3, 20},  // 8
             {12, 13,  2, -1},  // 9
             { 8, -1, -1, -1},  // 10
             {-1, -1, -1,  1},  // 11
             {-1, 15,  9,  6},  // 12
             {-1, -1,  9, -1},  // 13
             {-1, -1,  7, -1},  // 14
             {25, 16, 12, -1},  // 15
             {-1, -1, -1, 15},  // 16
             {21, -1, -1, -1},  // 17
             {23, -1, -1, -1},  // 18
             {-1, 20, -1, -1},  // 19
             {26,  8, -1, 19},  // 20
             {23, 17,  0, -1},  // 21
             {-1, 24, -1, -1},  // 22
             {28, 18, 21, -1},  // 23
             {-1, 25, 22,  1},  // 24
             {35, -1, 15, 24},  // 25
             {32, 27, 20, -1},  // 26
             {33, 36, -1, 26},  // 27
             {30, 29, 23, -1},  // 28
             {-1, -1, -1, 28},  // 29
             {28, 31, -1, 34},  // 30
             {-1, -1, -1, 30},  // 31
             {26, -1, -1, -1},  // 32
             {-1, -1, 27, -1},  // 33
             {-1, -1, 30, -1},  // 34
             {-1, -1, 25, -1},  // 35
             {-1, -1, -1, 27}   // 36
         };
         unsigned char percept=0;
         int ii, result=0, node_new=(node_curr>=0?maze[node_curr][direct]:0);
         if (node_new<0) {
             percept=16;
             result=3;
             if (visit==NVISIT-1) printf(" x");
         }
         else {
             node_curr=node_new;
             assert(node_curr<sizeof(maze)/sizeof(*maze));
             for (ii=0; ii<4; ii++)
                 if (maze[node_curr][ii]>=0) percept|=1 << ii;
             if (node_curr==31) {
                 if (!is_gf) {
                     is_gf=1;
                     result=1;
                 }
             }
             else if (node_curr==36) {
                 is_gf=0;
                 result=2;
             }
             if (visit==NVISIT-1)
                 printf(" %d:%s%s%s%s%s", node_curr,
                        (percept & (1 << DIRECT_NORTH))?"N":"",
                        (percept & (1 << DIRECT_EAST ))?"E":"",
                        (percept & (1 << DIRECT_SOUTH))?"S":"",
                        (percept & (1 << DIRECT_WEST ))?"W":"",
                        result==1?"(***)":"");
             if (result==2) node_curr=-1;
         }
         if (percept_p) *percept_p=percept;
         return result;
     }
     
     
     static QSMM_INSTR_META_CLASS(move) {
         const char *ccp;
         int rc;
         enum direct_e direct=0;
         if (QSMM_HAS_INSTR_CLASS(qsmm_evt))
             qsmm_get_eh_instr_param(qsmm,sizeof(direct),&direct);
         switch (qsmm_evt) {
             case QSMM_EVT_INSTR_CLASS_INIT:
                 switch (direct) {
                     case DIRECT_NORTH: ccp="north"; break;
                     case DIRECT_EAST:  ccp="east";  break;
                     case DIRECT_SOUTH: ccp="south"; break;
                     case DIRECT_WEST:  ccp="west";  break;
                     default: assert(0);
                 }
                 qsmm_set_eh_instr_param_str_f(qsmm,"%s",ccp);
                 qsmm_set_eh_noutcome(qsmm,17);
                 break;
             case QSMM_EVT_ACTIVATE: {
                 unsigned char percept=0;
                 rc=opaque_maze(direct,&percept);
                 qsmm_time_delta(qsmm,1);
                 switch (rc) {
                     case 0:
                     case 3:
                         break;
                     case 1:
                         qsmm_set_la_sig(qsmm,0,1);
                         n_gold_found++;
                         break;
                     case 2: {
                         qsmm_sig_t sig_la=0;
                         qsmm_get_la_sig(qsmm,0,&sig_la);
                         if (sig_la) qsmm_spur_delta(qsmm,1,1);
                         qsmm_return_to_caller_node(qsmm);
                         break;
                     }
                     default:
                         assert(0);
                 }
                 if (rc!=2) {
                     if (rc!=3) path_len++;
                     qsmm_set_instr_outcome(qsmm,percept);
                 }
                 break;
             }
         }
         return 0;
     }
     
     
     static QSMM_INSTR_CLASS_SET(walker) {
         switch (qsmm_evt) {
             case QSMM_EVT_ENT_INIT: {
                 enum direct_e direct;
                 direct=DIRECT_NORTH, QSMM_REG_INSTR_CLASS_PARAM(move,direct);
                 direct=DIRECT_EAST,  QSMM_REG_INSTR_CLASS_PARAM(move,direct);
                 direct=DIRECT_SOUTH, QSMM_REG_INSTR_CLASS_PARAM(move,direct);
                 direct=DIRECT_WEST,  QSMM_REG_INSTR_CLASS_PARAM(move,direct);
                 qsmm_set_nstate_max(qsmm,__FUNCTION__,NSTATE);
                 QSMM_NODE_CREATE(0);
                 break;
             }
             case QSMM_EVT_NODE_ENTER: {
                 unsigned char percept=0;
                 qsmm_set_la_sig(qsmm,0,0);
                 opaque_maze(DIRECT_NORTH,&percept);
                 break;
             }
         }
         return 0;
     }
     
     
     int main(int argc, char **argv) {
         const char *ccp;
         int rc, seed=0, exit_code=1;
         qsmm_t qsmm=0;
         FILE *file_mat_goto_p=0, *file_mat_action_p=0;
         struct qsmm_desc_s desc;
         struct qsmm_dump_mat_goto_desc_s dump_mat_goto_desc;
         memset(&desc,0,sizeof(desc));
         desc.dont_use_instr_class_weights=1;
         desc.is_large_env=1;
         desc.is_large_opt=1;
         desc.nspur=2;
         desc.stack_sz_max=1;
         desc.ngram_env_la_sz=1;
         desc.nsig_ngram_env_la=2;
         desc.sparse_fill_max=0.2;
         desc.compat=1;
         if ((rc=qsmm_create(&desc,&qsmm))<0)
             ERREXIT("qsmm_create: %s",qsmm_err_str(rc));
         QSMM_REG_INSTR_META_CLASS(qsmm,move,0);
         QSMM_REG_INSTR_CLASS_SET(qsmm,walker,0);
         qsmm_engine_create(qsmm);
         if (argc>1 && (seed=atoi(argv[1]))<0) {
             qsmm_set_random(qsmm,1);
             seed=-seed;
         }
         qsmm_rng_seed(qsmm_get_rng(qsmm),seed);
         for (visit=0; visit<NVISIT; visit++) qsmm_node_call_default(qsmm,0,0);
         printf("\nn_gold_found=%d\npath_len=%d\nn_gold_found/path_len=%.8f\n",
                n_gold_found, path_len, (double) n_gold_found/path_len);
         if (!(file_mat_goto_p=fopen(ccp="mat_goto","w")) ||
             !(file_mat_action_p=fopen(ccp="mat_action","w")))
             ERREXIT("%s: failed to open the file for writing",ccp);
         memset(&dump_mat_goto_desc,0,sizeof(dump_mat_goto_desc));
         dump_mat_goto_desc.do_print_prob[QSMM_PROB_LEARNT]=1;
         qsmm_mat_goto_dump(qsmm,0,&dump_mat_goto_desc,file_mat_goto_p);
         qsmm_mat_action_dump(qsmm,0,0,file_mat_action_p);
         exit_code=0;
     
     Exit:
         qsmm_destroy(qsmm);
         if (file_mat_action_p) fclose(file_mat_action_p);
         if (file_mat_goto_p) fclose(file_mat_goto_p);
         return exit_code;
     }

Below there is given sample program output for the completely random mode of operation of the program.

     $ ./maze -1
      0:NE 21:NES 23:NES 21:NES x 17:N 21:NES x 23:NES 28:NES 30:NEW x x 34
     :S x x x x x x x x x x 30:NEW 34:S x x x 30:NEW 34:S x 30:NEW 28:NES 3
     0:NEW x x 31:W(***) x 30:NEW 31:W 30:NEW 34:S x 30:NEW 34:S x x x x x
     x x x x x x x x x x x x 30:NEW x x x 31:W x x x x 30:NEW x 31:W 30:NEW
      x 34:S x x x x x x x x x x x x x 30:NEW x x 28:NES 29:W 28:NES x 30:N
     EW 31:W x x x x x x x x x x 30:NEW 31:W x x 30:NEW 31:W x x x x 30:NEW
      x 28:NES 29:W x 28:NES 30:NEW x 34:S x x 30:NEW 31:W x 30:NEW 28:NES
     23:NES x 18:N 23:NES 18:N x x 23:NES 28:NES x 29:W x x 28:NES 23:NES 1
     8:N x x x x x x x x x x x x 23:NES 18:N x x x x x x 23:NES 28:NES 29:W
      28:NES 29:W 28:NES 30:NEW 31:W x x 30:NEW 28:NES x 30:NEW x 28:NES 29
     :W 28:NES x 23:NES x 18:N x 23:NES 21:NES x 17:N x x x x x 21:NES 0:NE
      1:NEW 11:W x x x x x x x 1:NEW x x 0:NE x 1:NEW x 11:W x x x 1:NEW 11
     :W 1:NEW 24:ESW 25:NSW x 15:NES 25:NSW 15:NES 16:W x 15:NES 12:ESW 15:
     NES 12:ESW 15:NES 16:W x x x x x x x 15:NES x 16:W x x 15:NES x x 16:W
      15:NES 16:W 15:NES x x 16:W x x x x x 15:NES x 16:W x x 15:NES 16:W x
      x x x x x 15:NES x 12:ESW 15:NES 16:W x x x x x x x 15:NES 12:ESW 15:
     NES 16:W x 15:NES 16:W x x x 15:NES 25:NSW 35:S x x x x 25:NSW 24:ESW
     22:E x x x 24:ESW 25:NSW 35:S x x 25:NSW 24:ESW 22:E x x x 24:ESW 22:E
      x 24:ESW 1:NEW 24:ESW 1:NEW 0:NE x 21:NES 23:NES 18:N x x x 23:NES x
     28:NES 23:NES 21:NES 23:NES 28:NES 30:NEW 34:S x x x x x 30:NEW 31:W x
      x 30:NEW x 28:NES 23:NES 21:NES 23:NES 18:N x x x x x x x x x x 23:NE
     S 21:NES 0:NE x 1:NEW 0:NE x 1:NEW 0:NE x 1:NEW 24:ESW 25:NSW 24:ESW 2
     5:NSW 35:S x x 25:NSW x x x 35:S x x x x x x x x x x 25:NSW 35:S 25:NS
     W x 15:NES 12:ESW x 9:NES x x 12:ESW 9:NES 13:S 9:NES 12:ESW 15:NES 12
     :ESW 15:NES 12:ESW x 15:NES 12:ESW 15:NES 25:NSW 35:S 25:NSW x 15:NES
     16:W x x x x 15:NES 25:NSW 35:S x 25:NSW 15:NES x 25:NSW 15:NES 25:NSW
      24:ESW 25:NSW 35:S x 25:NSW 35:S 25:NSW 35:S x x x x x 25:NSW 15:NES
     x 25:NSW 24:ESW 1:NEW 24:ESW 22:E x x x x x x x 24:ESW 25:NSW 15:NES x
      x 25:NSW x 35:S x x x x x x x x x x x x 25:NSW x 24:ESW 1:NEW 11:W 1:
     NEW 11:W x x x x x 1:NEW 11:W 1:NEW 24:ESW x 1:NEW 11:W x x x x x x x
     1:NEW x 11:W x x 1:NEW x 24:ESW 25:NSW 24:ESW x x 1:NEW x 11:W 1:NEW x
      24:ESW 22:E x x x x 24:ESW 22:E x x x x x x x 24:ESW 1:NEW x 11:W x 1
     :NEW 0:NE 21:NES 17:N 21:NES 17:N x x x x x x 21:NES x 23:NES 21:NES 2
     3:NES 21:NES x 0:NE x x x 1:NEW 24:ESW 1:NEW 11:W x x 1:NEW 24:ESW 25:
     NSW 24:ESW x 1:NEW 11:W 1:NEW x 0:NE x 21:NES 17:N x 21:NES x 17:N 21:
     NES x x x 17:N x x 21:NES 23:NES 18:N x x x x x x x 23:NES 21:NES 0:NE
      x x 1:NEW 0:NE x 1:NEW 11:W x x x x 1:NEW 24:ESW 25:NSW 24:ESW x 1:NE
     W 11:W 1:NEW 11:W x x x x x x x 1:NEW 11:W 1:NEW 11:W 1:NEW 24:ESW 22:
     E x x x x x x x x x x x x x x 24:ESW x 22:E x x x x 24:ESW 25:NSW 24:E
     SW 25:NSW 24:ESW x x 22:E 24:ESW 22:E x x x x x x x x x 24:ESW 1:NEW 2
     4:ESW x 25:NSW x 24:ESW 22:E 24:ESW 1:NEW 11:W x x 1:NEW 11:W x x x 1:
     NEW 24:ESW x x x 1:NEW x 0:NE 21:NES 17:N x x x x x x x x x x x 21:NES
      x 23:NES x 18:N 23:NES 18:N 23:NES 28:NES 30:NEW x 31:W x 30:NEW 28:N
     ES 23:NES 28:NES x 30:NEW 28:NES 29:W 28:NES x x 23:NES 21:NES 0:NE x
     x x 1:NEW 24:ESW 25:NSW 35:S x x x 25:NSW 35:S x x x x x x x x 25:NSW
     15:NES 25:NSW 35:S x 25:NSW 35:S 25:NSW 15:NES 16:W x x 15:NES 16:W x
     x x x 15:NES 12:ESW 9:NES 13:S x x x x x 9:NES 13:S x 9:NES 13:S 9:NES
      13:S x x x x x x x x x x x 9:NES 13:S x 9:NES 2:E x x x x x x x x x x
      x x x 9:NES x 12:ESW 6:ESW x 7:NSW 6:ESW 5:E x x x x 6:ESW x 5:E 6:ES
     W 12:ESW 15:NES 12:ESW 15:NES 12:ESW 15:NES 16:W x x x x x x x x x x x
      15:NES 16:W x 15:NES x x 25:NSW 15:NES 12:ESW x 6:ESW 5:E x x x 6:ESW
      5:E x x x 6:ESW 5:E x x x x 6:ESW 12:ESW 9:NES x 2:E x x 9:NES 2:E x
     x x 9:NES 12:ESW x 6:ESW x 12:ESW x 15:NES x 12:ESW 6:ESW x 12:ESW 15:
     NES 12:ESW x 15:NES x 12:ESW x 9:NES 2:E x x x x x x x 9:NES 2:E 9:NES
      12:ESW 15:NES 12:ESW 9:NES 12:ESW 6:ESW x 12:ESW 6:ESW x 5:E x x 6:ES
     W 12:ESW 6:ESW 12:ESW 9:NES 12:ESW 9:NES 2:E x 9:NES 12:ESW 9:NES 12:E
     SW x 6:ESW 12:ESW 15:NES 25:NSW x 15:NES 25:NSW 35:S 25:NSW 24:ESW 1:N
     EW x 24:ESW x x 22:E x x x 24:ESW 1:NEW 11:W x x 1:NEW 24:ESW 1:NEW 24
     :ESW 25:NSW 35:S x x x x 25:NSW 35:S 25:NSW 24:ESW 22:E 24:ESW 1:NEW x
      0:NE x x x x 1:NEW 0:NE 1:NEW x 11:W 1:NEW x x 24:ESW 25:NSW 35:S x 2
     5:NSW 24:ESW 25:NSW x x 15:NES 16:W x x x 15:NES 16:W x x x x x x x x
     15:NES 16:W x x x x x x x x x 15:NES 25:NSW x x 15:NES 12:ESW 9:NES 2:
     E 9:NES 2:E x x x x x 9:NES 13:S x x x 9:NES x 2:E 9:NES x 12:ESW 9:NE
     S x x 2:E x x x 9:NES 2:E x x 9:NES 2:E x x 9:NES 13:S x x 9:NES 12:ES
     W x 6:ESW 12:ESW 15:NES x 25:NSW 15:NES x 25:NSW 15:NES x x x 16:W 15:
     NES 16:W x x x x 15:NES 12:ESW 9:NES 12:ESW 9:NES 2:E x x x 9:NES 12:E
     SW 9:NES x x 2:E x 9:NES x 12:ESW 15:NES 12:ESW 9:NES 13:S x 9:NES 12:
     ESW 9:NES x 12:ESW x 9:NES 2:E x 9:NES x 2:E x x 9:NES 2:E 9:NES x 13:
     S x x x x 9:NES 13:S x x x x 9:NES 13:S x x x x 9:NES 13:S x 9:NES 12:
     ESW 9:NES 12:ESW 9:NES 13:S x x x x x x x x x 9:NES 12:ESW x x 6:ESW 7
     :NSW 6:ESW 5:E x 6:ESW 7:NSW x 6:ESW 12:ESW 15:NES 25:NSW x 15:NES 12:
     ESW 15:NES 12:ESW 9:NES 2:E 9:NES 12:ESW 9:NES 13:S x 9:NES 13:S x 9:N
     ES 13:S x x 9:NES 12:ESW x x 9:NES 13:S 9:NES 13:S x 9:NES 13:S 9:NES
     12:ESW 15:NES 16:W 15:NES 25:NSW x x 35:S 25:NSW 35:S x x 25:NSW x x 2
     4:ESW 22:E x x 24:ESW 22:E x 24:ESW 22:E x x 24:ESW 25:NSW 15:NES 16:W
      x 15:NES 25:NSW x 35:S x 25:NSW 35:S 25:NSW 35:S x 25:NSW x 15:NES 16
     :W 15:NES 16:W x x x 15:NES 16:W x x x 15:NES 12:ESW 9:NES 2:E 9:NES x
      2:E x 9:NES 13:S x x x x x x x x x 9:NES 12:ESW 6:ESW 7:NSW 14:S x x
     x 7:NSW x 3:NEW 7:NSW 6:ESW 7:NSW 14:S x x x x x x x x x x x 7:NSW 6:E
     SW 5:E x x x 6:ESW 12:ESW x x 9:NES x 12:ESW x 9:NES 12:ESW 9:NES x 13
     :S x 9:NES x 13:S x x x 9:NES 12:ESW 9:NES 13:S 9:NES 2:E x x x 9:NES
     2:E 9:NES 13:S x x x 9:NES 13:S x 9:NES 2:E x 9:NES 13:S x x x 9:NES x
      13:S 9:NES 12:ESW 15:NES x 12:ESW 9:NES x 12:ESW x 9:NES 13:S x 9:NES
      x 13:S x x 9:NES x 13:S x x x x x x 9:NES x 12:ESW 6:ESW 7:NSW x 3:NE
     W 8:NSW x 3:NEW 4:W x x x x 3:NEW x 7:NSW 3:NEW 8:NSW 3:NEW 4:W x x x
     x x x x 3:NEW 4:W x x x x x x x x x x x x x x x x x x x x x x x 3:NEW
     4:W x x x x x x 3:NEW 4:W x 3:NEW 4:W x x x x x 3:NEW x x 8:NSW x 3:NE
     W 8:NSW 10:N x x x 8:NSW 10:N x x x x x x x 8:NSW 3:NEW 8:NSW 3:NEW 8:
     NSW 20:NEW 19:E x x x 20:NEW x x 8:NSW 10:N x 8:NSW 10:N x 8:NSW 3:NEW
      4:W 3:NEW 7:NSW x 14:S x x x x 7:NSW 6:ESW 5:E 6:ESW 12:ESW x 9:NES x
      13:S x x x x 9:NES 13:S x x x x x x 9:NES 13:S x 9:NES 12:ESW 9:NES 2
     :E x x 9:NES 12:ESW x 6:ESW 7:NSW 6:ESW 12:ESW x x x 9:NES x 12:ESW 6:
     ESW 7:NSW 6:ESW 12:ESW 15:NES 16:W 15:NES 12:ESW 15:NES 12:ESW 9:NES 1
     2:ESW 9:NES 13:S 9:NES 2:E 9:NES 2:E x 9:NES x x 2:E x x x x x x x x 9
     :NES x 2:E x x x x x x 9:NES 13:S 9:NES x 12:ESW 9:NES 2:E x x 9:NES 1
     3:S x x x 9:NES 12:ESW 9:NES 13:S x x 9:NES x 12:ESW 15:NES 12:ESW x 6
     :ESW 7:NSW 14:S x x x x x x x x x x x x x 7:NSW 3:NEW x 7:NSW 6:ESW 7:
     NSW 14:S x x 7:NSW 6:ESW x 7:NSW 3:NEW 8:NSW x 20:NEW x 8:NSW x 10:N x
      x x x x 8:NSW 10:N x x x x x x 8:NSW 20:NEW 26:NES 32:N 26:NES x x 32
     :N x 26:NES 27:NEW 33:S 27:NEW 33:S x x x x x 27:NEW 36:W
     n_gold_found=297
     path_len=257582
     n_gold_found/path_len=0.00115303

Below there is given sample program output for the normal mode of operation of the program.

     $ ./maze 1
      0:NE x 21:NES x 23:NES x x 21:NES 23:NES 28:NES 29:W x x 28:NES x 23:
     NES 28:NES 30:NEW 34:S x x x 30:NEW 28:NES x x 30:NEW 31:W(***) 30:NEW
      31:W 30:NEW 34:S x x x x x x 30:NEW 31:W 30:NEW 28:NES 23:NES x 21:NE
     S x 17:N x x x x x 21:NES 0:NE x 1:NEW x x 24:ESW x 25:NSW 35:S x x x
     x x 25:NSW 15:NES 16:W x x x x x 15:NES 16:W 15:NES x 12:ESW 6:ESW x x
      5:E 6:ESW x 7:NSW 14:S x x x 7:NSW 3:NEW 8:NSW 20:NEW 26:NES 27:NEW x
      x 26:NES 27:NEW 36:W
     n_gold_found=398
     path_len=37254
     n_gold_found/path_len=0.01068342
     

Results of invocation of the sample program using random seeds in the range 1 to 20 (normal mode of operation) and in the range -20 to -1 (completely random mode of operation) are represented in the table below.

       SEED NGF PATH_LEN NGF/PATH_LEN    SEED NGF PATH_LEN NGF/PATH_LEN
     ------ --- -------- ------------  ------ --- -------- ------------
          1 398    37254   0.01068342      -1 297   257582   0.00115303
          2 400    55424   0.00721709      -2 310   254296   0.00121905
          3 399    49060   0.00813290      -3 282   238204   0.00118386
          4 389    35382   0.01099429      -4 285   245584   0.00116050
          5 400    34992   0.01143118      -5 284   238040   0.00119308
          6 398   117074   0.00339956      -6 290   227870   0.00127266
          7 395    41422   0.00953600      -7 288   229944   0.00125248
          8 399    46858   0.00851509      -8 287   243174   0.00118022
          9 399    41146   0.00969718      -9 280   238668   0.00117318
         10 396    53364   0.00742073     -10 299   230718   0.00129595
         11 394    37840   0.01041226     -11 297   228010   0.00130257
         12 399    34672   0.01150784     -12 284   237060   0.00119801
         13 399    58556   0.00681399     -13 286   233272   0.00122604
         14 397    47514   0.00835543     -14 274   239664   0.00114327
         15 400    37706   0.01060839     -15 272   235452   0.00115522
         16 398    42292   0.00941076     -16 296   234588   0.00126179
         17 393    52206   0.00752787     -17 284   219864   0.00129171
         18 398    30352   0.01311281     -18 297   230014   0.00129123
         19 395    50552   0.00781374     -19 285   245476   0.00116101
         20 399    75942   0.00525401     -20 288   243924   0.00118070
     ------ --- -------- ------------  ------ --- -------- ------------
        AVG 397    48980  [0.00811039]    AVG 288   237570  [0.00121333]
     STDDEV 2.8    19221   0.00231747  STDDEV 9.0     9114   0.00005491

As it can be seen from the table, the average amount of gold found per one move in the labyrinth for 20 invocations of the sample program in the normal mode of operation is about 7 times greater than in the completely random mode of operation.


Next: , Previous: Multinode Model, Up: Top

5 Assembler Programs

In QSMM, assembler programs are in the first place the means of specifying probability profiles of nodes of multinode model in the algorithmic form. In the second place, they are the means of printing and analyzing learned state models of the nodes. In the third place, they are the means of fetching learned probabilities from the state models.

The use of assembler programs relies heavily on instruction meta-classes, instruction class sets, and instruction classes registered for a multinode model. An instruction meta-class name and a textual representation of instruction class parameters are used to code an assembler instruction in an assembler program.


Next: , Up: Assembler Programs

5.1 Basic Datatypes

A memory representation of an assembler program is referred to by a program handle.

— Data type: qsmm_prg_t

This is a type for a program handle. It is a pointer, so variables of this type can have zero value. Functions qsmm_node_disasm and qsmm_parse_asm_source_* allocate a new program handle. Function qsmm_prg_destroy frees an existing program handle. After allocating a program handle, it can be passed to API functions that take argument qsmm_prg_t until the handle is freed.

To destroy a memory representation of an assembler program, the following function can be used.

— Function: void qsmm_prg_destroy (qsmm_prg_t prg)

This function destroys a memory representation of an assembler program specified by program handle prg. After the destruction, the program handle must not be used. If prg is 0, then the function will do nothing.

The QSMM framework provides limited capabilities to work with instructions that are contained in a memory representation of an assembler program. An assembler instruction is referred to by an instruction handle.

— Data type: qsmm_instr_t

This is a type for an instruction handle. It is a pointer, so variables of this type can have zero value. The handle of an existing instruction can be obtained by functions qsmm_get_prg_instr and qsmm_get_instr_nested (see Inspecting an Assembler Program).


Next: , Previous: Basic Datatypes, Up: Assembler Programs

5.2 Assembler Program Syntax

An assembler program consists of lines. Empty lines can be used to decorate the program. Characters on a line, which start with the first character ‘;’ not within a string literal (that literal can be a part of instruction parameters), are considered a comment. For proper identification of continuation of multiline comments, align characters ‘;’, which start every line of a multiline comment, one under another.

If the first character on a line is not a whitespace character, then it must be the start of a label definition. There are two types of labels supported: location labels, which definitions end with character ‘:’, and data labels, which definitions do not end with that character. The first character of a label must be a letter or ‘_’. Every subsequent character of the label (except for the last character ‘:’ in the definition of location label) must be a letter, a digit, or ‘_’. A definition of a location label can be on a separate line or can be followed by an instruction after one or more whitespace characters. A definition of a data label must be followed by a ‘prob’, ‘probeq’, or ‘probls’ keyword after one or more whitespace characters.

If the line starts with one or more whitespace characters, then they can be followed by an instruction. An instruction consists of an instruction name followed by optional instruction parameters after one or more whitespace characters. Whitespace characters in instruction parameters not within string literals are ignored.

Below there is given a sample assembler program.

     L1:     stt
             jprob   0.5, L2         ; jump with probability 0.5
             user    0               ; a user instruction
             jmp     L1
     
     L2:     user    1               ; a user instruction
             jmp     L1

There are two types of sections that can be used in an assembler program: ‘.data’ section and ‘.code’ section. In a ‘.data’ section probability variables and probabilities lists can be defined (see Using Probability Variables and Using Probabilities Lists). To every probability variable or a probabilities list a data label corresponds. In a ‘.code’ section there can be program instructions. Sections are marked by ‘.data’ and ‘.code’ keywords that must be on lines of their own (after one or more whitespace characters at the beginning of lines). There can be several ‘.data’ and ‘.code’ sections defined in an assembler program. They are merged into a single ‘.data’ section and a single ‘.code’ section. By default, it is assumed that an assembler program starts with a ‘.code’ section.

The assembler supports the ‘line’ directive, which can be programmed explicitly or generated by the assembler preprocessor (see Using the Assembler Preprocessor). That directive can change current line number in the source file, the name of the source file, the stack of include locations, and the stack of macro expansion locations tracked by the assembler. However, changing the stack of include locations and the stack of macro expansion locations is not documented in this manual, because the corresponding syntax of the ‘line’ directive will likely be changed in future versions of the QSMM package. Information changed by the ‘line’ directive is used when printing error, warning, and note messages.

The directive must be placed on a separate line (after one or more whitespace characters at the beginning of the line) and should be in one of the following formats:

             line    line_number
             line    line_number, file_name

In the first case, the directive changes the number of the next line in the current source file to line_number. In the second case, the directive changes the number of the next line to line_number and the name of current source file to file_name. The value of file_name must be a (quoted) string literal.


Next: , Previous: Assembler Program Syntax, Up: Assembler Programs

5.3 Assembler Instructions

In QSMM, assembler instructions are divided into three categories: built-in instructions, user instructions, and mixed type instructions. The assembler understands the built-in instructions without prior definition of corresponding instruction classes and meta-classes. The built-in instructions are case, casels, choice, end, jmp, joe, jprob, and stt. The built-in instructions do not necessarily induce code that is to be executed by some kind of a machine: they can be interpreted as control words that affect the structure of the machine.

User instructions must be declared in an application program using corresponding instruction classes and meta-classes. Execution of actions associated with user instructions has to be implemented in event handler functions of corresponding instruction meta-classes.

Instructions abort, lookup, nop, and nop1 are ones of mixed type. Instructions abort, lookup, and nop1 can be generated by the disassembler, but assembling them requires defining corresponding instruction classes and meta-classes, which implementation can be specific to your application program. In certain cases nop instructions can be implicitly generated by the assembler, but the assembling will fail if there is no corresponding instruction class or meta-class defined.


Next: , Up: Assembler Instructions

5.3.1 jmp Instruction

The instruction has syntax

             jmp     loc_label

and specifies that control has to be transferred to location label loc_label. The location label has to be defined elsewhere as follows:

     loc_label:


Next: , Previous: jmp Instruction, Up: Assembler Instructions

5.3.2 jprob Instruction

The instruction must be written in one of the following forms:

             jprob   number, loc_label
             jprob   var_name, loc_label

The instruction specifies that the control has to be transferred to location label loc_label with probability number, which must be in the range 0 to 1 (inclusive), or with probability stored in variable var_name. Probability number must be specified either in fixed-point or exponential notation.

The instruction effectively sets a profile probability in an action emission matrix or one or more profile probabilities in a state transition matrix. If a contiguous block of jprob instructions is prepended with an stt instruction, then those jprob instructions will set profile probabilities in the action emission matrix. If a contiguous block of jprob instructions is not prepended with an stt instruction, then those jprob instructions will set profile probabilities in the state transition matrix. See stt Instruction, for more information.

If the assembler considers that a user instruction should be assembled at a particular place, but it encounters a contiguous block of jprob instructions there, then the assembler will assemble a nop instruction and treat that contiguous block as possible transitions to states made after invocation of that nop instruction.

Let us consider a block of jprob instructions like this:

             jprob   prob1, L1
             jprob   prob2, L2
             jprob   prob3, L3

Jump to location label L1 will be made with probability prob1. Jump to the second jprob instruction will be made with probability 1–prob1. Jump to location label L2 will be made with probability (1–prob1)*prob2. Jump to the third jprob instruction will be made with probability (1–prob1)*(1–prob2). Jump to location label L3 will be made with probability (1–prob1)*(1–prob2)*prob3. Jump to an instruction that follows the third jprob instruction will be made with probability (1–prob1)*(1–prob2)*(1–prob3).


Next: , Previous: jprob Instruction, Up: Assembler Instructions

5.3.3 choice Instruction Block

The instruction block has format

             choice
             case    ...
             case    ...
             ...
             end     choice

It consists of a choice instruction, one or more case instructions, and an end choice instruction. Instructions case and end choice must not be prepended with location labels.

Each case instruction must be written in one of the following forms:

             case    number, loc_label
             case    var_name, loc_label

The instruction specifies that the control has to be transferred to location label loc_label with probability number, which must be in the range 0 to 1 (inclusive), or with probability stored in variable var_name. Probability number must be specified either in fixed-point or exponential notation. If there is a location label defined at the beginning of a choice instruction block, then you must not use that label in case instructions within the block.

The instruction block effectively sets profile probabilities in an action emission matrix or a state transition matrix. If a choice instruction block is prepended with an stt instruction, then the instruction block will set profile probabilities in the action emission matrix. If a choice instruction block is not prepended with an stt instruction, then the instruction block will set profile probabilities in the state transition matrix. See stt Instruction, for more information.

If the assembler considers that a user instruction should be assembled at a particular place, but it encounters a choice instruction block there, then the assembler will assemble a nop instruction and treat the choice instruction block as possible transitions to states made after invocation of that nop instruction.

As opposed to a contiguous block of jprob instructions, a choice instruction block allows to specify jump probabilities in a direct way. Let us consider a choice instruction block like this:

             choice
             case    prob1, L1
             case    prob2, L2
             case    prob3, L3
             end     choice

Jump to location label L1 will be made with probability prob1. Jump to location label L2 will be made with probability prob2. Jump to location label L3 will be made with probability prob3. Jump to an instruction that follows the choice instruction block will be made with probability 1–prob1prob2prob3. Compare this example to the corresponding example with a block of jprob instructions in the previous subsection.


Next: , Previous: choice Instruction Block, Up: Assembler Instructions

5.3.4 casels Instruction

The instruction has syntax

             casels  list_name, loc_label_1, ..., loc_label_N

and specifies that control has to be transferred to one of location labels loc_label_1, ..., loc_label_N with probabilities, which are elements of probabilities list list_name. The number of location labels must be equal to the length of the probabilities list. See Using Probabilities Lists, for more information. If there is a location label assigned to a casels instruction, then you must not use that label in arguments of the casels instructions.

The list of parameters of a casels instruction can be splitted into multiple lines. To indicate that a line of parameters list is continued on the next line, terminate a line, which is continued, with a comma after the name of probabilities list or a location label. This is shown in the following example:

             casels  ls,
                     L1, L2, L3, L4, L5,
                     L6, L7, L8, L9, L10

A casels instruction effectively sets profile probabilities in an action emission matrix or a state transition matrix. If a casels instruction is prepended with an stt instruction, then the casels instruction will set profile probabilities in the action emission matrix. If a casels instruction is not prepended with an stt instruction, then the casels instruction will set profile probabilities in the state transition matrix. See stt Instruction, for more information.

If the assembler considers that a user instruction should be assembled at a particular place, but it encounters a casels instruction there, then the assembler will assemble a nop instruction and treat the casels instruction as a specification of possible transitions to states after invocation of that nop instruction.


Next: , Previous: casels Instruction, Up: Assembler Instructions

5.3.5 joe Instruction

The instruction has syntax

             joe     outcome, loc_label

and specifies that control has to be transferred to location label loc_label if an outcome of the last instruction invoked is equal to outcome. The total number of instruction outcomes is specified by the event handler function of instruction meta-class during initialization of the instruction class.

If the assembler considers that a user instruction should be assembled at a particular place, but it encounters a contiguous block of joe instructions there, then the assembler will assemble a nop instruction, which normally does not change the outcome of the last instruction invoked, and treat the contiguous block as a place where analysis of that outcome is performed.


Next: , Previous: joe Instruction, Up: Assembler Instructions

5.3.6 stt Instruction

The instruction must be written in one of the following forms:

             stt
             stt     state_name

The instruction marks the beginning of a state of an assembler program. The state corresponds to a state of a node, into which that assembler program can be loaded. The state might have a name specified by (quoted) string literal state_name. An assembler program must not contain states with duplicate names. See Loading a Parsed Program into a Node, for a description of functions that retrieve the name of a node state by its index and retrieve the index of a node state by its name.

An stt instruction indicates in which matrix of a node probabilities specified by other instructions of the assembler program should be stored: in the action emission matrix or the state transition matrix. If a node is assembled with a restriction that the action emission matrix must define deterministic choice of instruction for every state of the node, and there is no need to assign a name to a state of an assembler program, then an stt instruction, which marks the start of the state, can be safely omitted in the assembler program. Probabilities specified by jprob, case, and casels instructions that follow a place where the stt instruction was omitted will be stored in the state transition matrix of the node.

If a node is assembled without a restriction that its action emission matrix must define deterministic choice of instruction for every node state, then omitting stt instructions in the assembler program will cause the first unassembled instruction encountered while assembling to become the start of the next state. However, assembling a node is not always performed by consecutive processing the instructions of an assembler program from its beginning to the end, and the best practice is to specify stt instructions for all states, so the programmer will know exactly where each state begins. To support this approach, the assembler may generate a warning where it has to begin assembling a state, but it encounters no stt instruction there. Note that inserting stt instructions into an assembler program may require rearranging the program.

There are four possible places where an stt instruction can be inserted.

  1. Before a user instruction or a mixed type instruction:
                      stt     [state_name]
                      user or mixed type instruction
    

    This means that in a state marked by the stt instruction a specified user or mixed type instruction will be invoked. Effectively, the action emission matrix will define deterministic choice of the instruction in the state: it will contain profile probability 1 for that state and that instruction and profile probability 0 for that state and all other instructions. If you need to mark the start of a state, but there is no user or a mixed type instruction to insert after the stt instruction, then you may insert two instructions at the start of the state: stt and nop, or stt and nop1. A user or mixed type instruction after the stt instruction must not have a location label defined, to which a jump from elsewhere is made.

  2. Before a block of jprob instructions:
                      stt     [state_name]
                      jprob   prob1, L1
                      jprob   prob2, L2
                      ...
                      jprob   probN, LN
                      user or mixed type instruction
                      ...
              L1:     user or mixed type instruction
                      ...
              L2:     user or mixed type instruction
                      ...
              LN:     user or mixed type instruction
                      ...
    

    This means that in a state marked by the stt instruction one of specified user or mixed type instructions will be invoked. All those instructions must have different combinations of instruction name and instruction parameters. Effectively, the action emission matrix will contain profile probabilities of invocation of the instructions in that state. However, in the general case, the profile probabilities will not be equal to those specified in the jprob instructions (see jprob Instruction, for more information). None of the jprob instructions just after the stt instruction must have location labels defined, to which jumps from elsewhere are made.

  3. Before a choice instruction block:
                      stt     [state_name]
              
                      choice
                      case    prob1, L1
                      case    prob2, L2
                      ...
                      case    probN, LN
                      end     choice
              
                      user or mixed type instruction
                      ...
              L1:     user or mixed type instruction
                      ...
              L2:     user or mixed type instruction
                      ...
              LN:     user or mixed type instruction
                      ...
    

    This means that in a state marked by the stt instruction one of specified user or mixed type instructions will be invoked. All those instructions must have different combinations of instruction name and instruction parameters. Effectively, the action emission matrix will contain profile probabilities of invocation of the instructions in that state. The profile probabilities, except for the profile probability of an instruction just after the choice instruction block, will be equal to those specified in case instructions. A choice instruction after the stt instruction must not have a location label defined, to which a jump from elsewhere is made.

  4. Before a casels instruction:
                      stt     [state_name]
                      casels  list_name, L1, L2, ..., LN
                      user or mixed type instruction
                      ...
              L1:     user or mixed type instruction
                      ...
              L2:     user or mixed type instruction
                      ...
              LN:     user or mixed type instruction
                      ...
    

    This means that in a state marked by the stt instruction one of specified user or mixed type instructions will be invoked. All those instructions must have different combinations of instruction name and instruction parameters. Effectively, the action emission matrix will contain profile probabilities of invocation of the instructions in that state. The profile probabilities, except for the profile probability of an instruction just after the casels instruction, will be equal to those contained in probabilities list list_name. A casels instruction after the stt instruction must not have a location label defined, to which a jump from elsewhere is made.

When inserting stt instructions into a program, adhere to the following program structure. A user or mixed type instruction can be followed by a contiguous block of joe instructions which analyze an outcome of that instruction. Every joe instruction should specify a conditional jump to a contiguous block of jprob instructions or to a choice instruction block or to a casels instruction or to another state directly. The contiguous block of joe instructions should be followed by a contiguous block of jprob instructions or a choice instruction block or a casels instruction or by a jmp instruction that transfers control to another state. The contiguous blocks of jprob instructions or contiguous blocks of case instructions within the choice instruction blocks or the casels instructions should specify probabilities of transitions to other states (stored in the state transition matrix). Every contiguous block of jprob instructions or a choice instruction block or a casels instruction should be followed by a jmp instruction that transfers control to another state or should be followed by another state directly. Such program structure is represented by the following example.

             user or mixed type instruction
     
             ; A contiguous block of `joe' instructions.
             ; Each of labels L1, L2, ..., LN may be defined at the start of a
             ; contiguous block of `jprob' instructions or at the start of a
             ; `choice' instruction block or at a `casels' instruction or at
             ; the start of a state.
     
             joe     outcome1, L1
             joe     outcome2, L2
             ...
             joe     outcomeN, LN
             joe     outcomeN1, LN1      ; Conditional jump to a sample
                                         ; `casels' instruction.
             joe     outcomeN2, LN2      ; Conditional jump to a sample
                                         ; `choice' instruction block.
             joe     outcomeN3, LN3      ; Conditional jump to a sample
                                         ; contiguous block of `jprob'
                                         ; instructions.
     
             ; An optional contiguous block of `jprob' instructions (may also
             ; be a `choice' instruction block or a `casels' instruction)
             ; followed by a `jmp' instruction. Each of labels SA1, SA2, ...,
             ; SAN, SANN should be defined at the start of a state.
     
             jprob   probA1, SA1
             jprob   probA2, SA2
             ...
             jprob   probAN, SAN
             jmp     SANN
     
             ; A sample `casels' instruction that specifies probabilities of
             ; transitions to states for a source state, in which the user or
             ; mixed type instruction is invoked, for that instruction, and
             ; for its outcome outcomeN1. Probabilities list list_name, which
             ; contains N probabilities, should be defined in the `.data'
             ; section of the assembler program. Each of labels SB1, SB2, ...,
             ; SBN, SBNN should be defined at the start of a state.
     
     LN1:    casels  list_name, SB1, SB2, ..., SBN
             jmp     SBNN
     
             ; A sample `choice' instruction block that specifies
             ; probabilities of transitions to states for a source state, in
             ; which the user or mixed type instruction is invoked, for that
             ; instruction, and for its outcome outcomeN2. Each of labels SC1,
             ; SC2, ..., SCN, SCNN should be defined at the start of a state.
     
     LN2:    choice
             case    probC1, SC1
             case    probC2, SC2
             ...
             case    probCN, SCN
             end     choice
     
             jmp     SCNN
     
             ; A sample contiguous block of `jprob' instructions that
             ; specifies probabilities of transitions to states for a source
             ; state, in which the user or mixed type instruction is invoked,
             ; for that instruction, and for its outcome outcomeN3. Each of
             ; labels SD1, SD2, ..., SDN, SDNN should be defined at the start
             ; of a state.
     
     LN3:    jprob   probD1, SD1
             jprob   probD2, SD2
             ...
             jprob   probDN, SDN
             jmp     SDNN                ; This instruction will not be needed
                                         ; if it specifies a jump to a state
                                         ; that goes just after the
                                         ; instruction.

After a user instruction or a mixed type instruction there can be no joe instructions at all. In this case, the instruction should be immediately followed by a contiguous block of jprob instructions or a choice instruction block or a casels instruction or by a jmp instruction that transfers control to another state or should be followed by another state directly. Such arrangement of instructions is represented by the example below.

             user or mixed type instruction
     
             ; An optional contiguous block of `jprob' instructions (may also
             ; be a `choice' instruction block or a `casels' instruction) that
             ; specifies probabilities of transitions to states for a source
             ; state, in which the user or mixed type instruction is invoked,
             ; and for that instruction. Each of labels S1, S2, ..., SN, SNN
             ; should be defined at the start of a state.
     
             jprob   prob1, S1
             jprob   prob2, S2
             ...
             jprob   probN, SN
             jmp     SNN                 ; This instruction will not be needed
                                         ; if it specifies a jump to a state
                                         ; that goes just after the
                                         ; instruction.
     
             ; The start of another state.

Adherence to the above structure of an assembler program will simplify understanding how your assembler program is converted to a probability profile stored in the state transition matrix and the action emission matrix of a node. Such understanding may be necessary to develop an assembler program that can be trained efficiently.


Next: , Previous: stt Instruction, Up: Assembler Instructions

5.3.7 nop and nop1 Instructions

Mixed type instruction nop must have the corresponding instruction meta-class explicitly defined in the application program. The instruction is supposed to do nothing and not to change the outcome of the previous instruction invoked by a node. The instruction meta-class can be defined using the following block of code:

     static QSMM_INSTR_META_CLASS(nop) {
         switch (qsmm_evt) {
             case QSMM_EVT_INSTR_CLASS_INIT:
                 qsmm_set_eh_noutcome(qsmm,0);
                 break;
         }
         return 0;
     }

The instruction can be explicitly inserted into an assembler program after stt instructions where needed. The instruction can also be generated implicitly while assembling the program, in which case the corresponding warning will be produced.

Ideally, there should be no implicitly generated nop instructions in your assembler program. You have to analyze warnings produced while assembling and remove instructions, which do nothing useful, or insert stt instructions where needed, rearranging your program if necessary, or insert nop instructions into appropriate places explicitly.

Mixed type instruction nop1 is supposed to have a single outcome and do nothing. The instruction meta-class can be defined using the following block of code:

     static QSMM_INSTR_META_CLASS(nop1) {
         return 0;
     }

This instruction (instead of a nop instruction) can be explicitly inserted after an stt instruction when there is no need to preserve the outcome of the previous instruction invoked by a node and the outcome should be reset to 0. The use of the nop1 instruction instead of the nop instruction reduces the number of profile probabilities written to the state transition matrix, making it simpler and decreasing the amount of memory needed to store the matrix (if map storage is used). This instruction can be generated by the disassembler at the beginning of an assembler program.


Next: , Previous: nop and nop1 Instructions, Up: Assembler Instructions

5.3.8 lookup Instruction

The instruction is the mixed type one, which can be generated when disassembling a node of a multinode model that has positive length of the look-ahead signal segment. That length is specified when creating the multinode model and can be retrieved using function qsmm_get_ngram_env_la_sz.

The instruction has syntax

             lookup  position

The instruction sets the outcome to a look-ahead signal at given zero-based position.

When the length of the look-ahead signal segment of a multinode model is positive, assembler programs cannot be loaded into nodes of the multinode model. Therefore, if you need to assemble a program that contains lookup instructions (e.g. generated by the disassembler), you have to create a model with the zero-length look-ahead signal segment. You have to define instruction meta-class ‘lookup’ that fetches signals at given positions from an array used as a substitute for the look-ahead signal segment. You also need to register necessary instruction classes derived from instruction meta-class ‘lookup’, the number of which is equal to the number of possible values of position.


Next: , Previous: lookup Instruction, Up: Assembler Instructions

5.3.9 abort Instruction

The instruction is the mixed type one, which can be generated by the disassembler after a user instruction when the disassembler has no information what should be executed after that user instruction. The information is typically absent when the user instruction was executed only once and did not return control, or when subsequent instructions were discarded according to parameters of disassembling.

When you need to assemble programs generated by the disassembler that contain abort instructions, the following approach can be used. First, make the disassembler generate a single abort instruction at the program end, to which there are jumps from other places of the program. After an abort instruction at the end of the program there will be a jump to the start of the program. Second, implement the abort instruction as the nop1 instruction that does nothing. The control will be transferred to the program start when it is not known what to execute. Maybe such looping will be appropriate in your situation.


Previous: abort Instruction, Up: Assembler Instructions

5.3.10 User Instructions

An assembler program can contain user instructions, which correspond to instruction classes that are held in the instruction class set of a node and perform application-specific operations.

The parameters string of a user instruction can be splitted into multiple lines at positions of commas contained not within string literals. To indicate that a line of the parameters string is continued on the next line, terminate a line, which is continued, with a comma.

Before searching an instruction class in the set, the parameters string is normalized according to rules described in Setting the Instruction Parameters String.


Next: , Previous: Assembler Instructions, Up: Assembler Programs

5.4 Disassembling a Node

The state transition matrix and the action emission matrix of a node can be converted to a representation in the form of an assembler program. This conversion process is called disassembling a node.

In practice, disassembling a node is typically performed after training the node. Before the training, an assembler program, which specifies a probability profile for the state transition matrix and the action emission matrix of the node, could be loaded into the node. In this case, to simplify the analysis of results of training the node, it is often desirable for a developer to obtain a disassembled program, which is an assembler program previously loaded into the node, but with profile probabilities in jprob and case instructions changed to probabilities learned while training the node. This mode of disassembling a node is implemented starting from QSMM version 1.15 and is called disassembling using an assembler program template. To turn this mode on, pass the QSMM_ASM_TEMPLATE flag to function qsmm_node_asm when loading an assembler program into a node that will be disassembled later (see Loading a Parsed Program into a Node). If an assembler program was not previously loaded into a node or was loaded without specification of the QSMM_ASM_TEMPLATE flag, then disassembling the node can only be performed in a classic mode, which was the only mode implemented in QSMM before version 1.15.

To disassemble a node, the following function can be used.

— Function: int qsmm_node_disasm (qsmm_t model, int node, struct qsmm_disasm_desc_s *desc_p, qsmm_prg_t *prg_p)

This function disassembles node of multinode model according to parameters specified in *desc_p and stores an allocated handle of disassembled program in *prg_p. If desc_p is 0, then default disassembling parameters will be used.

If node is previously assembled with the QSMM_ASM_TEMPLATE flag or if node uses a source probability profile provided by another node assembled with the QSMM_ASM_TEMPLATE flag, then the disassembled program will be an assembler program stored in a node assembled with the QSMM_ASM_TEMPLATE flag as the template assembler program. In the disassembled program profile probabilities in jprob and case instructions of the template assembler program are replaced with probabilities of type desc_p->prob_type (if desc_p is not 0) or of type QSMM_PROB_AGGR (if desc_p is 0).

In the current implementation, if desc_p is not 0, then *desc_p will not be modified as a result of the function call. However, in future versions of the package the contents of *desc_p could be modified by the function, e.g. statistics on the disassembling process could be written there.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Either argument prg_p is 0 or parameters specified in *desc_p (if desc_p is not 0) are invalid.
QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state.

A structure, a pointer to which is an argument of function qsmm_node_disasm, is described below.

— Structure: qsmm_disasm_desc_s

This structure specifies parameters of disassembling. It contains the following fields.

— Field: char use_stt_start

If not 0, then generate an stt instruction at the beginning of an assembler program when the program starts with a lookup instruction, and generate stt and nop1 instructions at the beginning of a program when the program starts with a jprob instruction or a choice instruction block. The default is not to generate. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: char use_stt_state

If not 0, then generate stt instructions at the beginnings of states. The default is not to generate. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: char use_stt_lookup

If not 0, then generate stt instructions before lookup instructions. The default is not to generate. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: char use_stt_abort

If not 0, then generate stt instructions before abort instructions. This will not be very useful unless there is not more than one abort instruction in the program, which is controlled by field use_abort_1. The default is not to generate. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: char use_choice

If not 0, then generate a choice instruction block instead of a contiguous block of jprob instructions when the number of jprob instructions in the block is greater than one. The default is to generate. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: char use_abort_1

If is 0, then generate each time an abort instruction just after a user instruction when it is not known what to execute after the user instruction. If not 0, then generate each time a jump after such user instruction to a single abort instruction at the end of the program and generate a jump to the program start after that abort instruction. If not 0 and there are no jumps to an abort instruction at the end of the program, then that abort instruction followed by a jump to the program start will not be generated. The default is to generate an abort instruction just after a user instruction. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: char do_retain_goto_1

If not 0, then retain the most probable transition to another state for a quadruple that consists of a source state, a user or mixed type instruction invoked, an outcome of that instruction, and the contents of the look-ahead signal segment when discarding transitions with probabilities less than specified in field prob_goto_min. This ensures that for every quadruple there remains at least one transition to another state. Field prob_type specifies a type of probabilities being analyzed. The default is not to retain the most probable transition. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: char do_retain_action_1

If not 0, then retain the most probable user or mixed type instruction that can be executed in a state when discarding instructions with probabilities less than specified in field prob_action_min. This ensures that for every state there remains at least one user or mixed type instruction that can be executed in the state, i.e. the state will not be removed because it does not contain user or mixed type instructions. Field prob_type specifies a type of probabilities being analyzed. The default is not to retain the most probable user or mixed type instruction. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: char do_calc_prob[QSMM_PROB_COUNT]

An array that specifies additional types of probabilities to calculate for jprob and case instructions, which can be printed in comments for those instructions. Indices of the array are the elements of enumeration qsmm_prob_e (except for the last element) described in Generating an Optimal Action. If an element of the array has a non-zero value, then probabilities of the corresponding type will be calculated and stored in the instructions. Probabilities of a type specified in field prob_type are calculated always, regardless of the value of field do_calc_prob. The default is not to calculate probabilities of additional types.

— Field: long fq_goto_min

The minimum frequency of transition to another state for a quadruple that consists of a source state, a user or mixed type instruction invoked, an outcome of that instruction, and the contents of the look-ahead signal segment. Transitions with lesser frequencies are discarded out of hand. The frequencies might not be incremented for transitions performed deterministically. It is not recommended to set this field to a positive value when disassembling a node, into which an assembler program was loaded, because the assembler program might define deterministic transitions, which frequencies could be zero and which would be discarded. The default minimum frequency is 0. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: long fq_action_min

The minimum frequency of invocation of user or mixed type instruction in a state. Instructions invoked lesser number of times are discarded out of hand. The frequencies might not be incremented for instruction invocations performed deterministically. It is not recommended to set this field to a positive value when disassembling a node, into which an assembler program was loaded, because the assembler program might define deterministic instruction invocations which frequencies could be zero. The default minimum frequency is 0. The value of this field is ignored if a node is disassembled using an assembler program template.

— Field: double prob_goto_min

When disassembling a node using an assembler program template, this field specifies the minimum probability in jprob and case instructions that correspond to the state transition matrix of the node. Instructions with lesser probabilities will be removed from the disassembled program, although they are present in the assembler program template.

When disassembling a node without using an assembler program template, this field specifies the minimum probability of transition to another state for a quadruple that consists of a source state, a user or mixed type instruction invoked, an outcome of that instruction, and the contents of the look-ahead signal segment. Transitions with lesser probabilities are discarded after discarding transitions with frequencies less than a frequency specified in field fq_goto_min and after renormalizing transition probabilities. When discarding transitions with lesser probabilities, if do_retain_goto_1 is not 0, then the most probable transition will be retained.

Field prob_type specifies a type of probabilities being analyzed. The default minimum probability is 0.

— Field: double prob_action_min

When disassembling a node using an assembler program template, this field specifies the minimum probability in jprob and case instructions that correspond to the action emission matrix of the node. Instructions with lesser probabilities will be removed from the disassembled program, although they are present in the assembler program template.

When disassembling a node without using an assembler program template, this field specifies the minimum probability of invocation of user or mixed type instruction in a state. Instructions with lesser invocation probabilities are discarded after discarding instructions with frequencies less than a frequency specified in field fq_action_min and after renormalizing the invocation probabilities. When discarding instructions with lesser invocation probabilities, if do_retain_action_1 is not 0, then the most probable instruction will be retained.

Field prob_type specifies a type of probabilities being analyzed. The default minimum probability is 0.

— Field: enum qsmm_prob_e prob_type

A type of probabilities to calculate for jprob and case instructions.

When disassembling a node using an assembler program template, those probabilities are compared with probabilities in fields prob_goto_min and prob_action_min to determine whether to discard specific jprob and case instructions.

When disassembling a node without using an assembler program template, this field specifies the type of probability, by which jprob and case instructions within corresponding instruction blocks are sorted in descending order. This field also specifies the type of probabilities of transitions to other states and the type of probabilities of invocation of user and mixed type instructions, which are compared with probabilities in fields prob_goto_min and prob_action_min to perform discarding when necessary.

See Generating an Optimal Action, for a description of elements of enumeration qsmm_prob_e. The default type of probabilities is QSMM_PROB_AGGR.

An instance of structure qsmm_disasm_desc_s, a pointer to which is passed to function qsmm_node_disasm, should be zeroed using function memset before setting values of structure fields.

When disassembling a node without using an assembler program template, the following comments are generated for instructions in a disassembled program.

Note the following regarding the frequencies.

When disassembling a node without using an assembler program template, to generate the most probable completely deterministic program, set fields prob_goto_min, prob_action_min, do_retain_goto_1, and do_retain_action_1 of structure qsmm_disasm_desc_s to 1.

Below there is given an example of calling function qsmm_node_disasm to generate a program for subsequent assembling. It is assumed that there is no assembler program loaded into a node for which function qsmm_node_disasm is called.

     qsmm_prg_t prg=0;
     struct qsmm_disasm_desc_s disasm_desc;
     memset(&disasm_desc,0,sizeof(disasm_desc));
     // Insert `stt' instructions at appropriate places.
     disasm_desc.use_stt_start=1;
     disasm_desc.use_stt_state=1;
     disasm_desc.use_stt_lookup=1;
     disasm_desc.use_stt_abort=1;
     // Make probabilistic jumps to look intelligible.
     disasm_desc.use_choice=1;
     // Simplify handling unexplored control paths.
     disasm_desc.use_abort_1=1;
     // Do not produce code that was never executed.
     disasm_desc.fq_goto_min=1;
     disasm_desc.fq_action_min=1;
     // Do not generate probabilistic jumps with
     // rounded probabilities less than 1%.
     disasm_desc.prob_goto_min=0.005;
     disasm_desc.prob_action_min=0.005;
     qsmm_node_disasm(qsmm,node,&disasm_desc,&prg);

To disassemble a node assembled with the QSMM_ASM_TEMPLATE flag, the above example can be reduced to the lesser number of lines because assignments to other fields are ignored in this mode.

     qsmm_prg_t prg=0;
     struct qsmm_disasm_desc_s disasm_desc;
     memset(&disasm_desc,0,sizeof(disasm_desc));
     // Remove `jprob' and `case' instructions with
     // rounded probabilities less than 1%.
     disasm_desc.prob_goto_min=0.005;
     disasm_desc.prob_action_min=0.005;
     qsmm_node_disasm(qsmm,node,&disasm_desc,&prg);


Next: , Previous: Disassembling a Node, Up: Assembler Programs

5.5 Inspecting an Assembler Program

To get the number of instructions contained in an assembler program, the following function can be used.

— Function: int qsmm_get_prg_ninstr (qsmm_prg_t prg)

This function returns the number of instructions contained in program prg. The returned value is always non-negative.

To get an instruction contained in an assembler program, the following function can be used.

— Function: qsmm_instr_t qsmm_get_prg_instr (qsmm_prg_t prg, int instr_idx)

This function returns the handle of an instruction contained in program prg at zero-based index instr_idx.

If instr_idx is negative or is greater than or equal to the number of instructions in the program, then 0 will be returned.

A type of an assembler instruction is specified using the following enumeration.

— Enumeration: qsmm_instr_e

This enumeration specifies a type of an assembler instruction. It contains the following elements.

QSMM_INSTR_USER
A user or mixed type instruction.
QSMM_INSTR_JMP
A jmp instruction.
QSMM_INSTR_JPROB
A jprob instruction.
QSMM_INSTR_CASE
A case instruction (within a choice instruction block).
QSMM_INSTR_CHOICE
A choice instruction.
QSMM_INSTR_CASELS
A casels instruction.
QSMM_INSTR_JOE
A joe instruction.
QSMM_INSTR_STT
An stt instruction.
QSMM_INSTR_END
An end choice instruction (at the end of a choice instruction block).

To get the type of an assembler instruction, the following function can be used.

— Function: enum qsmm_instr_e qsmm_get_instr_type (qsmm_instr_t instr)

This function returns the type of instruction instr.

Function qsmm_get_prg_instr does not return instructions of types QSMM_INSTR_CASE and QSMM_INSTR_END. It can return an instruction of type QSMM_INSTR_CHOICE instead, which represents a choice instruction block that contains nested case and end choice instructions. To get the number of instructions, nested in a choice instruction block, the following function can be used.

— Function: int qsmm_get_instr_nnested (qsmm_instr_t instr)

This function returns the number of instructions nested in instruction instr of type QSMM_INSTR_CHOICE. For instructions of other types, the function returns 0. The returned value is always non-negative.

To get the handle of a nested case or end choice instruction, the following function can be used.

— Function: qsmm_instr_t qsmm_get_instr_nested (qsmm_instr_t instr, int nested_idx)

This function returns the handle of an instruction nested in instruction instr of type QSMM_INSTR_CHOICE. A zero-based index of nested instruction is specified by nested_idx.

If instruction instr is not of type QSMM_INSTR_CHOICE, or nested_idx is negative or is greater than or equal to the number of instructions nested in instruction instr, then 0 will be returned.

An instruction can have location labels assigned to it, which are normally placed to the left of the instruction. A location label assigned to a casels instruction can be used to fetch probabilities, which correspond to jump labels specified in arguments of that instruction. A location label assigned to a choice instruction block can be used to fetch probabilities that correspond to case instructions within the block. The fetched probabilities could be learned during the node training. To get the number of location labels assigned to an instruction, the following function can be used.

— Function: int qsmm_get_instr_nlabel (qsmm_instr_t instr)

This function returns the number of location labels assigned to instruction instr, which are normally placed to the left of instruction. The returned value is always non-negative.

To get a location label by a location label index, the following function can be used.

— Function: const char * qsmm_get_instr_label (qsmm_instr_t instr, int label_idx)

This function returns a location label at zero-based index label_idx assigned to instruction instr. That label is normally placed to the left of instruction. If label_idx is negative or is greater than or equal to the number of location labels assigned to the instruction, then 0 will be returned.

To get the name of a probabilities list associated with an instruction, a function described below can be used. See Using Probabilities Lists, for more information on probabilities lists.

— Function: const char * qsmm_get_instr_ls_name (qsmm_instr_t instr)

This function returns the name of a probabilities list associated with instruction instr of type QSMM_INSTR_CASELS. If instruction instr has a type other than QSMM_INSTR_CASELS, then 0 will be returned.

The purpose of the function described below is to get the number of elements in a probabilities list defined in an assembler program. The retrieved number of elements can be used to allocate an array for fetching probabilities learned during the node training that correspond to jump labels specified in arguments of a casels instruction.

— Function: int qsmm_get_prg_ls_nprob (qsmm_prg_t prg, const char *ls_name)

This function returns the number of elements in probabilities list ls_name defined in program prg.

On success, a positive value is returned. If program prg does not contain probabilities list ls_name, then negative error code QSMM_ERR_NOTFOUND will be returned.


Next: , Previous: Inspecting an Assembler Program, Up: Assembler Programs

5.6 Printing an Assembler Program

To get a string representation of an instruction of an assembler program, the following function can be used.

— Function: int qsmm_instr_str (char *bufp, int bufsz, qsmm_instr_t instr, struct qsmm_dump_instr_desc_s *desc_p)

This function stores in buffer bufp of size bufsz bytes a string representation of instruction instr generated according to parameters specified in *desc_p. If desc_p is 0, then default parameters of generating the string representation will be used. The function may change either *desc_p or the instance of structure qsmm_dump_instr_desc_s where the default parameters are stored (when desc_p is 0).

On success, the function returns a non-negative number of bytes required for a string representation of the instruction, not counting trailing byte 0. The returned value greater than or equal to bufsz indicates that the string representation was truncated. If bufsz is greater than 0, then the truncated string representation of length bufsz–1 bytes will be written to bufp and terminated with byte 0. It is allowed to pass 0 for both bufp and bufsz to determine the length of the string representation of an instruction.

On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of bufsz is less than 0, or the value of bufsz is greater than 0 and bufp is 0, or desc_p is not 0 and parameters specified in *desc_p are invalid, or desc_p is 0 and the default parameters are invalid.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

There is an instance of structure qsmm_dump_instr_desc_s defined within the library, where the default parameters of generating a string representation of instruction are stored. To get a pointer to that instance, the following function can be used.

— Function: struct qsmm_dump_instr_desc_s * qsmm_get_default_dump_instr_desc ()

This function returns a pointer to default parameters of generating a string representation of an assembler instruction. The parameters can be changed using that pointer. The function never returns 0.

A structure, which specifies parameters of generating a string representation of instruction, is described below.

— Structure: qsmm_dump_instr_desc_s

This structure specifies parameters of generating a string representation of an assembler instruction. It contains the following fields.

— Field: char is_line_after_comment_above

If not 0, then if there is a comment above the instruction stored with the instruction and field do_print_comment_above has a non-zero value, then insert an empty line after the comment and before the instruction. This field is also used in a similar way to determine whether to insert an empty line after a comment above a definition of a probability variable or a probabilities list in an assembler program and before the definition. The default is to insert an empty line.

— Field: char is_line_after_multiline_comment_right

If not 0 and field do_print_comment_right has a non-zero value, then insert an empty line after an instruction, a jump label (in arguments of casels instruction), the definition of a probability variable, a probabilities list, or its element that has a multiline comment to the right if the next line in the assembler program contains a comment to the right. The default is to insert an empty line.

— Field: char is_space_after_comma

If not 0, then insert a space after every comma not within string literals in instruction parameters. This field is also used in a similar way to determine whether to insert a space after every comma in definitions of probabilities lists in an assembler program. The default is to insert a space.

— Field: char do_print_label

If not 0 and the instruction has a location label defined, then print the location label to the left of the instruction. If there are several location labels defined for the instruction, then all the labels except for the last one will be printed on separate lines. If there is no room between the last label and an instruction name, then the last label will also be printed on a separate line. The default is to print location labels.

— Field: char do_print_name

If not 0, then print an instruction name. The default is to print.

— Field: char do_print_param

If not 0, then print instruction parameters. The default is to print.

— Field: char do_print_comment_above

If not 0 and there is a comment above the instruction stored with the instruction, then print that comment. This field is also used in a similar way to determine whether to print comments above definitions of probability variables and probabilities lists in an assembler program. The default is to print the comments.

— Field: char do_print_comment_right

If not 0 and there is a comment to the right of an instruction or a jump label (in arguments of casels instruction) stored with the instruction or the jump label, then print that comment. This field is also used in a similar way to determine whether to print comments to the right of definitions of probability variables, probabilities lists and their elements in an assembler program. The default is to print the comments.

— Field: char do_print_var

If not 0, then if there is a name of probability variable stored in a jprob or a case instruction and the value of field prob_type is equal to QSMM_PROB_PROFILE, then print that name of variable. Otherwise, print a probability. This field also determines whether to print names of probability variables in definitions of probabilities lists in an assembler program where those names were provided in the program source text. The default is to print names of probability variables when possible.

— Field: char do_print_state_name

If not 0, then if there is a state name stored with an stt instruction and field do_print_param has a non-zero value, then print the state name in that instruction. The default is to print state names.

— Field: char do_print_prob[QSMM_PROB_COUNT]

An array that specifies additional types of probabilities to print in comments for jprob and case instructions to the right. Indices of the array are the elements of enumeration qsmm_prob_e (except for the last element) described in Generating an Optimal Action. If an element of the array has a non-zero value, the index of the element is not equal to the value of field prob_type, and the value of field do_print_comment_right is non-zero, then probabilities of the corresponding type will be printed in the comments. When dumping an instruction of a program disassembled using an assembler program template, if that instruction is used in the template in an ambiguous context, then ‘?’ will be printed for the probabilities. The default is not to print probabilities in the comments.

— Field: int prob_prec

The number of digits after the decimal point to print for probabilities in jprob and case instructions and comments for those instructions to the right according to the value of field do_print_prob. It is also the number of digits after the decimal point to print for probabilities in definitions of probability variables, probabilities lists and their elements in an assembler program. If is a non-negative number, then fixed-point notation will be used. If is a negative number, then exponential notation with the number of digits after the decimal point equal to the absolute value of the field will be used. The default number of digits to print after the decimal point is equal to 2.

— Field: int col_name

If is a positive number, then a column to align the start of instruction name to. If not a positive number, then do not perform the alignment. This field also indicates a column to align the start of ‘.data’ and ‘.code’ directives, a column to align the start of comments above instructions, definitions of probability variables and probabilities lists, a column to align the start of a tail comment in an assembler program if the assembler program contains that comment. If not a positive number, then ‘.data’ and ‘.code’ directives and the comments will be aligned to column 1. The default is to align to column 9.

— Field: int col_param

If is a positive number, then a column to align the start of instruction parameters to. If not a positive number, then do not perform the alignment. The default is to align to column 17.

— Field: int col_comment

If is a positive number, then a column to align the start of a comment to the right of instruction to. If not a positive number, then do not perform the alignment. The default is to align to column 33.

— Field: int margin_right

If is a positive number, then a right margin column to use for comments. Longer comments will be splitted into multiple lines. If not a positive number, then do not split comments into multiple lines based on a right margin column. The default is not to split.

— Field: int margin_right_param

If is a positive number, then a right margin column to use for lists of arguments of user and casels instructions. Longer lists will be splitted into multiple lines. Commas not within string literals (those literals can be parts of arguments of user instructions) are used as places, at which splitting can be performed. If not a positive number, then do not split the lists into multiple lines based on a right margin column. The default right margin column is 30.

— Field: int nline_comment_right

Function qsmm_instr_str sets the value of this field. The function writes here the number of lines in a comment to the right of the instruction or the last jump label in arguments of a casels instruction. If field do_print_comment_right has zero value, the instruction or the last jump label does not have a comment to the right, or the instruction is a user instruction, which arguments list was splitted into multiple lines and which has a comment to the right with the number of lines less than the number of lines of the arguments list, then 0 will be written here. If field do_print_comment_right has a non-zero value and the instruction or the last jump label has a comment to the right, which was splitted into multiple lines according to the value of field margin_right, then a value greater than 1 will be written here (with the exception of a user instruction with a comment to the right that occupies the number of lines less than the number of lines of arguments list of the instruction). The value of this field is used by function qsmm_prg_dump to insert an empty line after an instruction that has a multiline comment to the right when the next instruction has a comment to the right.

— Field: int nline_param

Function qsmm_instr_str sets the value of this field. The function writes here the number of lines of arguments list of an instruction printed. The value of this field is used by function qsmm_prg_dump to determine whether to insert empty lines around multiline instructions.

— Field: enum qsmm_prob_e prob_type

A type of probabilities to print in jprob and case instructions. See Generating an Optimal Action, for a description of elements of enumeration qsmm_prob_e. When dumping an instruction of a program disassembled using an assembler program template, if that instruction is used in the template in an ambiguous context, then ‘?’ will be printed for the probability. The default type of probabilities is QSMM_PROB_AGGR.

Note: when converting a disassembled program or its specific instructions to a string representation, be sure to set fields do_print_prob and prob_type of structure qsmm_dump_instr_desc_s to values consistent with values of fields do_calc_prob and prob_type of structure qsmm_disasm_desc_s used when disassembling the program. Otherwise, zero probabilities will be printed.

To dump a program to a stream, the following function can be used.

— Function: int qsmm_prg_dump (qsmm_prg_t prg, struct qsmm_dump_prg_desc_s *desc_p, FILE *filep)

This function dumps program prg to stream filep according to parameters specified in *desc_p. If desc_p is 0, then default parameters of dumping a program will be used.

In the current implementation, *desc_p (if desc_p is not 0) or the instance of structure qsmm_dump_prg_desc_s where the default parameters are stored (if desc_p is 0), are not modified as a result of the function call. However, in future versions of the package those variables could be modified by the function, e.g. statistics on the dumping process could be written there.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Argument desc_p is not 0 and parameters specified in *desc_p are invalid, or desc_p is 0 and default parameters, a pointer to which is returned by function qsmm_get_default_dump_prg_desc, are invalid.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

There is an instance of structure qsmm_dump_prg_desc_s defined within the library, where the default parameters of dumping a program are stored. To get a pointer to that instance, the following function can be used.

— Function: struct qsmm_dump_prg_desc_s * qsmm_get_default_dump_prg_desc ()

This function returns a pointer to default parameters of dumping an assembler program. The parameters can be changed using that pointer. The function never returns 0.

A structure, which specifies parameters of dumping a program, is described below.

— Structure: qsmm_dump_prg_desc_s

This structure specifies parameters of dumping an assembler program. It contains the following fields.

— Field: char do_print_vars

If not 0 and the program contains definitions of probability variables and/or probabilities lists, then dump a ‘.data’ section with those definitions. The default is to dump.

— Field: char is_line_before_label

If not 0, then insert an empty line before a line with a location label definition. If an instruction has several location labels defined (which are dumped on separate lines), then an empty line will be inserted only before a line with the first location label definition. The default is to insert.

— Field: char is_line_before_isolated_comment

If not 0, then insert an empty line before a comment above an instruction or a definition of a probability variable or a probabilities list if the instruction or the definition has that comment, and insert an empty line before a comment at the end of the program if the program contains that comment and field do_print_comment_tail has a non-zero value. The default is to insert.

— Field: char are_lines_around_choice

If not 0, then insert empty lines before and after choice instruction blocks. The default is to insert.

— Field: char are_lines_around_multiline_param

If not 0, then insert empty lines before and after multiline user and casels instructions. The default is to insert.

— Field: char do_print_comment_tail

If not 0 and the program contains a comment at the end, then dump that comment. The default is to dump.

— Field: int col_var_type

If is a positive number, then a column to align the start of ‘prob’, ‘probeq’, and ‘probls’ keywords to. If not a positive number, then do not perform the alignment. The default is to align to column 17.

— Field: int col_var_val

If is a positive number, then a column to align the start of a probability variable or probabilities list value to. If not a positive number, then do not perform the alignment. The default is to align to column 25.

— Field: int col_var_comment

If is a positive number, then a column to align the start of a comment to the right of definition of a probability variable, a probabilities list, or its element. If not a positive number, then do not perform the alignment. The default is to align to column 33.

— Field: int margin_right_ls

If is a positive number, then a right margin column to use for probabilities lists defined with the help of ‘probls’ keywords. Longer lists will be splitted into multiple lines. If not a positive number, then do not split the lists into multiple lines based on a right margin column. The default right margin column is 30.

— Field: struct qsmm_dump_instr_desc_s * dump_instr_desc_p

If not 0, then parameters of dumping instructions of the program. If is 0, then use default parameters returned by function qsmm_get_default_dump_instr_desc.


Next: , Previous: Printing an Assembler Program, Up: Assembler Programs

5.7 Parsing an Assembler Program

The QSMM framework supports parsing an assembler program provided in a string buffer, in a file, or which should be read from a stream. The program is converted to a memory representation that can be inspected, printed, or loaded into a node.

— Function: int qsmm_parse_asm_source_buf (const char *in_p, const char *cwd_p, unsigned int flags, void *rez1, void *rez2, qsmm_msglist_t msglist, qsmm_prg_t *prg_p)
— Function: int qsmm_parse_asm_source_stream (FILE *filep, const char *cwd_p, unsigned int flags, void *rez1, void *rez2, qsmm_msglist_t msglist, qsmm_prg_t *prg_p)

Function qsmm_parse_asm_source_buf parses an assembler program, the source text of which is given using argument in_p in the form of a NULL-terminated string. Function qsmm_parse_asm_source_stream parses an assembler program, the source text of which is read from stream filep. The functions store an allocated handle of the memory representation of the program in *prg_p.

If msglist is not 0, then error, warning, and note messages generated during parsing will be written to message list msglist. If flags has a bit specified by mask QSMM_PARSE_ASM_PREPROCESS set, then the source program text will be first preprocessed by the assembler preprocessor. If cwd_p is not 0, then cwd_p will be used for the name of current working directory by the assembler preprocessor when resolving ‘include’ directives. If cwd_p is 0, then the actual current working directory will be used when resolving ‘include’ directives. Arguments rez1 and rez2 are reserved for future use and must be equal to 0.

The functions return a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Argument prg_p is 0.
QSMM_ERR_PRG
One or more errors were encountered in the source program text.
QSMM_ERR_ILSEQ
The source text of the assembler program cannot be converted to a wide string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_parse_asm_source_file (const char *fln, unsigned int flags, void *rez1, void *rez2, qsmm_msglist_t msglist, qsmm_prg_t *prg_p)

This function parses an assembler program, the source text of which is provided in a file named fln, and stores an allocated handle of the memory representation of the program in *prg_p. If msglist is not 0, then error, warning, and note messages generated during parsing will be written to message list msglist. If flags has a bit specified by mask QSMM_PARSE_ASM_PREPROCESS set, then the source program text will be first preprocessed by the assembler preprocessor. Arguments rez1 and rez2 are reserved for future use and must be equal to 0.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Argument prg_p is 0.
QSMM_ERR_LIBC
When accessing file fln, the operating system has returned an error. Variable errno holds the error code.
QSMM_ERR_PRG
One or more errors were encountered in the source program text.
QSMM_ERR_ILSEQ
The source text of the assembler program cannot be converted to a wide string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

See Creating a Message List, for a description of functions using which an empty message list can be created to pass it to functions qsmm_parse_asm_source_* to possibly fill it with messages and finally destroyed. See Dumping a Message List, for a description of a function using which that message list can be dumped to a stream.


Next: , Previous: Parsing an Assembler Program, Up: Assembler Programs

5.8 Loading a Parsed Program into a Node

An assembler program specifies a probability profile that can be loaded into nodes of multinode model. Loading a probability profile, which is specified by a parsed assembler program, into a node of multinode model is called assembling a node for short. The node must belong to a node class represented by an instruction class set that contains instruction classes for all user and mixed type instructions used in the assembler program. Parsed assembler programs can only be loaded into nodes of a multinode model that has the look-ahead signal segment of zero length.

Functions described further on in this section take argument flags that specifies modes of processing a parsed assembler program. This argument was introduced in QSMM version 1.15. The argument is a bitmask that can be defined as a subset of macros described below merged by bitwise “or.”

— Macro: QSMM_ASM_DETERM_OPT

Impose the following restriction on an action emission matrix that corresponds to the assembler program: for every node state the action emission matrix must define deterministic choice of an instruction class, which can be emitted in that node state. This mode will be turned on implicitly if field is_determ_opt of structure qsmm_desc_s has a non-zero value when creating the multinode model. In this mode, when processing the memory representation of an assembler program, states are assumed to begin just before user and mixed type instructions, and there is no need to mark unnamed states by stt instructions. If the assembler program contains stt instructions, they cannot be located just before jprob and casels instructions and choice instruction blocks.

— Macro: QSMM_ASM_TEMPLATE

When converting the memory representation of an assembler program to the state transition matrix and the action emission matrix of a node, store that memory representation in the node as an assembler program template. The template will be automatically used when disassembling the node by function qsmm_node_disasm. A disassembled program will be the template program for which profile probabilities in jprob and case instructions are replaced with probabilities of a type specified in field prob_type of structure qsmm_disasm_desc_s passed to function qsmm_node_disasm or of type QSMM_PROB_AGGR if default disassembling parameters are used. Field prob_goto_min of structure qsmm_disasm_desc_s specifies the minimum probability in jprob and case instructions that correspond to the state transition matrix. Field prob_action_min of structure qsmm_disasm_desc_s specifies the minimum probability in jprob and case instructions that correspond to the action emission matrix. Instructions with lesser probabilities are removed from the disassembled program, although they are present in the template program. Additional probabilities of types specified in field do_calc_prob of structure qsmm_disasm_desc_s can be calculated and then printed in comments for jprob and case instructions.

When storing an assembler program template into a node, casels instructions encountered in the memory representation of the assembler program are converted to choice instruction blocks or jprob instructions. If a casels instruction uses a probabilities list that contains more than one element, the instruction is converted to a choice instruction block. Otherwise, the instruction is converted to a jprob instruction. Such conversion makes possible obtaining probabilities associated with jump labels of casels instructions when disassembling the node.

— Macro: QSMM_ASM_VAR_OUT

Collect information on output probability variables and arrays in the assembler program. They all can be used to obtain probabilities learned during the node training. Output probability variables can be used to obtain probabilities that correspond to jprob and case instructions. Output probabilities arrays can be used to obtains probabilities that correspond to casels instructions and choice instruction blocks. See Output Variables, and Getting Output Probabilities Arrays, for more information.

— Macro: QSMM_ASM_VAR_AUX

Permit the use of auxiliary probability variables in the assembler program. Auxiliary probability variables are probability variables that are neither controlled nor output ones. Auxiliary probability variables are used merely for convenience in jprob and case instructions and definitions of probabilities lists and are replaced with their actual values in the process of assembling the node. See Auxiliary Variables, for more information.

To load the memory representation of an assembler program into a node, the following function can be used.

— Function: int qsmm_node_asm (qsmm_t model, int node, unsigned int flags, qsmm_prg_t prg, qsmm_msglist_t msglist)

This function loads a probability profile, specified by program prg, into node of multinode model, i.e. assembles the node. Modes of assembling the node are specified using bitmask flags (see above for a description of macros that correspond to bits of the bitmask taken into account). If msglist is not 0, then warning messages generated during the assembling will be written to msglist. The function clears statistics on the event history that might have been collected for the node. If the node already has a probability profile loaded, then that profile will be first unloaded. To clear statistics on the event history and to unload the profile, function qsmm_node_unload described in Unloading a Probability Profile is implicitly called.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_NOSTATE
A node with identifier node does not have enough states.
QSMM_ERR_MPROF
No room in the pool of probabilities lists in normal form of a large actor that represents the environment state identification engine or the instruction emitting engine. Sizes of the pools are specified in fields profile_pool_env_sz and profile_pool_opt_sz of structure qsmm_desc_s passed to function qsmm_create when creating the multinode model.
QSMM_ERR_PROFSRCP
A node with identifier node is the source of probability profile for other nodes. See Memory Efficient Cloning a Probability Profile, for more information on this mode.
QSMM_ERR_PRG
An error was encountered in program prg.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.

To successfully load a program into a node, the node must have the sufficient number of states. Otherwise, error QSMM_ERR_NOSTATE will be raised. The number of states of a node can be set using function qsmm_set_node_nstate, but it must not exceed the maximum number of states of nodes of instruction class set, which is specified using function qsmm_set_nstate_max. To determine, which number of states to pass to functions qsmm_set_node_nstate and qsmm_set_nstate_max, the following function can be used.

— Function: int qsmm_get_prg_nstate (qsmm_t model, const char *instr_class_set_name, unsigned int flags, qsmm_prg_t prg, qsmm_msglist_t msglist)

This function calculates the minimum number of states required for nodes of multinode model to successfully load program prg into them. Calculating is performed for nodes that belong to a node class represented by instruction class set instr_class_set_name. Modes of assembling the nodes are specified using bitmask flags (see the beginning of this section for a description of macros that correspond to bits of the bitmask taken into account). For a certainty, the value of flags passed to this function must be equal to the value of argument flags that will be passed to function qsmm_node_asm when loading program prg into particular nodes of the model. If msglist is not 0, then warning messages generated during the calculating will be written to msglist.

On success, the function returns a non-negative number of states. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_PRG
An error was encountered in program prg.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

Functions qsmm_node_asm and qsmm_get_prg_nstate may generate the following warning messages.

`nop' instruction has been inserted
A nop instruction has been inserted at the beginning of an implicitly defined state because the state does not start with a user or mixed type instruction.
a replacement `nop' instruction has been generated
After a jprob instruction, which has a jump probability greater than 0, there is a jmp instruction with a jump to that jprob instruction, and the jump location of the jprob instruction is not the next instruction. The jprob instruction was replaced with the nop instruction, and the address of the next instruction, which will be executed after that nop instruction, has been set to the jump location of the jprob instruction.
all valid outcomes have already been tested
Control will never reach an instruction after a joe instruction block because all possible outcomes of a user or mixed type instruction have already been tested in that block.
instruction does nothing and was replaced with the `nop' instruction
The jump location of a jprob instruction, which has a jump probability less than 1, is that jprob instruction, or the jump location of a jprob or a joe instruction is the next instruction. The instruction was replaced with the nop instruction.
list element at index idx has no effect
A jump label at zero-based index idx in arguments of a casels instruction specifies the transfer of control to the next instruction after the casels instruction. The jump label was ignored.
nested instruction at index idx: instruction does nothing
The jump location of a case instruction at zero-based index idx within a choice instruction block is the next instruction after that choice instruction block. The case instruction was ignored.
state is not marked by an `stt' instruction
The assembler wants to start assembling a state at this location, but there is no stt instruction specified here. This causes the state to be defined implicitly. The warning message will not be generated if field is_determ_opt of structure qsmm_desc_s has a non-zero value when creating the multinode model or if the node is assembled with the QSMM_ASM_DETERM_OPT flag.
test for outcome num is ignored, because the outcome has already been tested
A joe instruction, which performs a jump on outcome num, is ignored, because there was another joe instruction before that instruction, which also performs a jump on outcome num.
test for outcome num is ignored, because the outcome is out of range
A joe instruction, which performs jump on outcome num, has been ignored, because a user or mixed type instruction, which outcome is being tested, has the number of possible outcomes less than or equal to num.
unreachable state
Control will never reach the state. This warning can only be generated if field is_determ_opt of structure qsmm_desc_s has a non-zero value when creating the multinode model or if the node is assembled with the QSMM_ASM_DETERM_OPT flag or if the state is marked by an stt instruction.

An assembler program might have names assigned to its states by arguments of stt instructions. After loading the assembler program into a node, the index of a node state, which corresponds to the name of the state, can be retrieved using the following function.

— Function: int qsmm_get_node_state_by_name (qsmm_t model, const char *state_name, int node, int *state_p)

This function sets *state_p equal to the index of a state of a node of multinode model. The state is specified by name state_name assigned by the argument of an stt instruction in an assembler program loaded into the node. If state_p is 0, then *state_p will not be set. If state_p is not 0, and there is no assembler program loaded into the node or the assembler program does not contain a state named state_name, then *state_p will be set to -1.

If node uses a source probability profile provided by another node, then the function will retrieve the index of node state for an assembler program loaded into the latter node. See Memory Efficient Cloning a Probability Profile, for a detailed description of this mode.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_ILSEQ
The name of node state cannot be converted to a wide string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To retrieve the name of a node state by its index, the following function can be used.

— Function: int qsmm_get_node_state_name (qsmm_t model, int node, int state, const char **state_name_pp)

This function sets *state_name_pp to a state name specified by the argument of an stt instruction in an assembler program loaded into a node of multinode model. The state name corresponds to state of the node. If state_name_pp is 0, then *state_name_pp will not be set. If state_name_pp is not 0, and there is no name assigned to the state in the assembler program, then *state_name_pp will be set to 0.

If node uses a source probability profile provided by another node, then the function will retrieve the state name specified in an assembler program loaded into the latter node. See Memory Efficient Cloning a Probability Profile, for a detailed description of this mode.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of state is negative or is greater than or equal to the number of states of a node with identifier node.
QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_ILSEQ
The name of node state cannot be converted to a multibyte string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Loading a Parsed Program into a Node, Up: Assembler Programs

5.9 Using Probability Variables

Probability variables are the means of changing a probability profile loaded into a node, retrieving probabilities learned during the node training, and parametrizing instructions and probabilities lists used in an assembler program. There are three kinds of variables supported: controlled, output, and auxiliary. Output and auxiliary probability variables were introduced in QSMM version 1.15.


Next: , Up: Using Probability Variables

5.9.1 Variables in an Assembler Program

Probability variables must be defined in a ‘.data’ section of an assembler program. Every definition of a probability variable consists of a data label, which is the name of the variable, followed by the ‘prob’ keyword after one or more whitespace characters and by a variable value in the range 0 to 1 (inclusive) after one or more whitespace characters. Fixed-point and exponential notations of specifying values of the variables are supported. A ‘.data’ section followed by a ‘.code’ section may look like this:

             .data
     
     var_name_1      prob    val1
     var_name_2      prob    val2
     ...
     var_name_N      prob    valN
     
             .code

There can be multiple ‘.data’ sections defined in the program, e.g. within macros that are expanded by the assembler preprocessor.

Names of probability variables previously defined in a ‘.data’ section can be used in jprob and case instructions instead of fixed probability values:

             jprob   var_name, loc_label
             case    var_name, loc_label

For example, a choice instruction block may look like this:

             choice
             case    var1, L1
             case    var2, L2
             case    0.25, L3
             case    var1, L4
             end     choice

Names of probability variables can also be used in ‘probeq’ and ‘probls’ directives that define probabilities lists. See Using Probabilities Lists, for more information on probabilities lists.

In a ‘probeq’ directive, which defines a probabilities list that contains equal elements, a probability variable can be used as a second argument:

     ls_name         probeq  length, var_name

In a ‘probls’ directive, which defines a probabilities list that contains specific elements, probability variables can be used as the elements of the list. An example ‘probls’ directive with some list elements equal to values of probability variables may look like this:

     ls1             probls  var1, var1, var2, var2,
                             var3, var3, 0.05, 0.05

After converting the text of an assembler program to a memory representation of the program, the number of probability variables defined in the program can be obtained using the following function.

— Function: int qsmm_get_prg_nvar (qsmm_prg_t prg)

This function returns the number of probability variables defined in program prg using ‘prob’ directives. The returned value is always non-negative.

To get the name of a probability variable by its index in the memory representation of an assembler program, the following function can be used.

— Function: const char * qsmm_get_prg_var_name (qsmm_prg_t prg, int var_idx)

This function returns the name of a probability variable with index var_idx in program prg. If var_idx is negative or is greater than or equal to the number of probability variables defined in the program using ‘prob’ directives, then 0 will be returned.


Next: , Previous: Variables in an Assembler Program, Up: Using Probability Variables

5.9.2 Controlled Variables

Controlled probability variables are the means of changing a probability profile loaded into a node. For example, the changing could be performed as a result of reasoning or self-programming.

Controlled probability variables that correspond to profile probabilities in the state transition matrix can only be used when the environment state identification engine is represented by a small actor, which will take place when field is_large_env of structure qsmm_desc_s passed to function qsmm_create has zero value. Controlled probability variables that correspond to profile probabilities in the action emission matrix can only be used when the instruction emitting engine is represented by a small actor, which will take place when field is_large_opt of structure qsmm_desc_s has zero value.

Controlled probability variables must be registered in an instruction class set that represents a node class of the node. After registering the variables in the instruction class set, an assembler program, which contains references to a subset of those variables in its text, can be loaded into the node. Later, the application may change values of controlled probability variables of the node, thus changing its probability profile. Changing values of the variables can be performed when processing invocation of specific instructions by event handler functions of instruction meta-classes.

When changing the value of a controlled probability variable via API function calls, profile probabilities in the state transition matrix and/or in the action emission matrix, which correspond to occurrences of jprob and case instructions, where that variable is used for a probability, and to jump labels in arguments of casels instructions, for which that variable specifies a probability, are updated accordingly.

To register a controlled probability variable in an instruction class set, the following function can be used.

— Function: int qsmm_reg_var_prob (qsmm_t model, const char *instr_class_set_name, const char *var_name, int rez1)

This function registers a controlled probability variable named var_name in instruction class set instr_class_set_name of multinode model. Argument rez1 is reserved for future use and must be equal to 0.

On success, the function returns a non-negative index of the variable registered in the instruction class set. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_EXIST
Probability variable var_name is already registered in instruction class set instr_class_set_name.
QSMM_ERR_VIOLNODE
There exist nodes that belong to a node class represented by instruction class set instr_class_set_name. Therefore, changing the structure of the instruction class set is not allowed.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_NOSYS
The environment state identification engine and the instruction emitting engine are represented by large actors.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

Currently, there are no QSMM API functions that take as an argument an index of variable returned by function qsmm_reg_var_prob. Instead, functions, which query and set values of controlled probability variables, take the name of variable as an argument. To speed up querying and setting values of the variables, in future versions of the library counterpart functions can be introduced, to which the index of a variable instead of the name of the variable is passed.

To register a controlled probability variable when processing event QSMM_EVT_ENT_INIT by the event handler function of instruction class set, the following macro can be used.

— Macro: QSMM_REG_VAR_PROB (var_name)

This macro registers controlled probability variable var_name. The macro is expanded to:

          qsmm_reg_var_prob((qsmm), __FUNCTION__, (var_name), 0)

The macro is to be expanded from the event handler function of an instruction class set. The name of the event handler function must coincide with the name of the instruction class set, and normally it does coincide. There must be variable qsmm, which is a handle of the multinode model, defined in a function from which the macro is expanded. Normally, that variable is an argument of the event handler function.

To get names of controlled probability variables registered in an instruction class set, the following function can be used.

— Function: int qsmm_enum_var_prob (qsmm_t model, const char *instr_class_set_name, qsmm_enum_ent_callback_func_t callback_func, void *paramp)

This function enumerates controlled probability variables that are registered in instruction class set instr_class_set_name of multinode model. The process of enumeration is a repeated calling callback function callback_func, to which entity type QSMM_ENT_VAR_PROB, the name of a controlled probability variable, and user parameter paramp are passed. The type of the callback function is described in Enumerating Entities.

If the callback function returns a positive value, then the process of enumeration will be continued. If the callback function returns zero, then the process of enumeration will be terminated, and function qsmm_enum_var_prob will report success. If the callback function returns a negative value, then the process of enumeration will be terminated, and function qsmm_enum_var_prob will report failure.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
Instruction class set instr_class_set_name does not exist.
QSMM_ERR_TYPE
An entity named instr_class_set_name is not an instruction class set. The entity is an instruction meta-class.
QSMM_ERR_CALLBACK
The callback function did return an error.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To query or set the value of a controlled probability variable from an application program, the following functions can be used.

— Function: int qsmm_get_node_var_prob (qsmm_t model, const char *var_name, int node, double *valp)

This function sets *valp to the value of controlled probability variable var_name of node of multinode model. If valp is 0, then *valp will not be set.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node or a controlled probability variable named var_name does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_set_node_var_prob (qsmm_t model, const char *var_name, int node, double val)

This function sets the value of controlled probability variable var_name of a node of multinode model to val. However, the actual update of profile probabilities in the state transition matrix and/or in the action emission matrix of the node is not performed until function qsmm_node_var_realize (described further on in this subsection) is called.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of val is not finite or is negative or is greater than 1.
QSMM_ERR_NOTFOUND
A node with identifier node or a probability variable named var_name does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

After loading an assembler program into a node, controlled probability variables associated with the node take on initial values defined in the assembler program.

It is allowed to set values of controlled probability variables declared in an instruction class set, but not defined in the assembler program. It is also allowed to set values of controlled probability variables for a node, to which an assembler program was not loaded. Such assignments to variables do not cause changing profile probabilities in the state transition matrix and the action emission matrix of the node.

After assigning values to controlled probability variables by function qsmm_set_node_var_prob, the actual update of profile probabilities in the state transition matrix and the action emission matrix of the node can be performed using the following function.

— Function: int qsmm_node_var_realize (qsmm_t model, int node, int rez1)

This function updates the state transition matrix and/or the action emission matrix of a node of multinode model according to assignments to controlled probability variables of the node, which were previously made using function qsmm_set_node_var_prob, and then clears the queue of pending updates. Argument rez1 is reserved for future use and must be equal to 0.

For affected cycle types, values of field profile of structure qsmm_cycle_s are updated. For affected action choice states, values of fields nsig_pos and nsig_ctrl of structure qsmm_state_s are updated.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_PSUMGT1
The sum of probabilities of case instructions in a choice instruction block or the sum of elements of a probabilities list used by a casels instruction will exceed 1. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_PROFSRCP
A node with identifier node is the source of probability profile for other nodes. See Memory Efficient Cloning a Probability Profile, for more information on this mode.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.

To prevent relying on a rather weak concept of the mean number of actor's output signals described in Other Parameters of an Actor, it is recommended to use controlled probability variables in a somewhat restricted way when they specify either deterministic choices or do not change the number of choice alternatives if it is greater than 1. To achieve this, it is recommended to use jprob instructions when the potential number of choice alternatives is equal to 2 and use choice instruction blocks or casels instructions when the potential number of choice alternatives is greater than 2. At all places in the assembler program where the choice is not deterministic, there should be used certain fixed number of choice alternatives greater than 1.

Consider the following example with a jprob instruction:

             user    0               ; a user instruction
             jprob   var0, L1
             jmp     L2

If the number of alternatives of non-deterministic choices at all places in the assembler program is equal to 2, then correct values of controlled probability variable var0 will be 0, 0.5, and 1. Value 0 specifies deterministic jump to location label L2, value 1 specifies deterministic jump to location label L1, and value 0.5 provides two jump alternatives, which corresponds to the actual number of actor's output signals equal to 2. The concept of the actual number of output signals is described in Other Parameters of an Actor.

Consider the following example with a choice instruction block:

             user    0               ; a user instruction
     
             choice
             case    var1, L1
             case    0.25, L2
             case    var3, L3
             case    var4, L4
             case    var5, L5
             case    var6, L6
             case    var7, L7
             end     choice
     
             jmp     L8

Correct sets of values of controlled probability variables var1, var3, var4, var5, var6, and var7 are the sets in which exactly two or three variables have values 0.25 and all other variables have values 0. Such sets correspond to the actual number of actor's output signals equal to 4, which should be the number of alternatives of every non-deterministic choice made in the assembler program.


Next: , Previous: Controlled Variables, Up: Using Probability Variables

5.9.3 Output Variables

Output probability variables are used to retrieve probabilities learned during training a node. Variables of this kind need not be registered in the instruction class set. Information on output probability variables is automatically fetched from the memory representation of an assembler program when loading the assembler program into a node by function qsmm_node_asm, to which the QSMM_ASM_VAR_OUT flag is passed. An output probability variable can be a controlled probability variable.

Not every probability variable encountered in the memory representation of an assembler program will be registered as an output probability variable of the node. The first restriction is that the variable must be used in only one jprob or case instruction. The second restriction is that the variable must be used in unambiguous context. If a probability variable violates at least one of these restrictions, but is registered in the instruction class set as a controlled probability variable, then it will be only a controlled probability variable. If the QSMM_ASM_VAR_AUX flag is passed to function qsmm_node_asm, a probability variable violates at least one of the above restrictions and is not registered in the instruction class set as a controlled probability variable, then it will be treated as an auxiliary variable. If the QSMM_ASM_VAR_AUX flag is not passed to function qsmm_node_asm, a probability variable violates at least one of the above restrictions and is not registered in the instruction class set as a controlled probability variable, then an error will be reported.

Every output probability variable corresponds either to the state transition matrix or to the action emission matrix of a node. The type of a matrix is defined using an enumeration described below. The enumeration is introduced in QSMM version 1.16.

— Enumeration: qsmm_mat_e

This enumeration specifies a type of matrix. It contains the following elements.

QSMM_MAT_GOTO
The state transition matrix.
QSMM_MAT_ACTION
The action emission matrix.

To get the type of a matrix, to which an output probability variable corresponds, a function described below can be used. The function is introduced in QSMM version 1.16.

— Function: int qsmm_get_node_var_prob_mat (qsmm_t model, const char *var_name, int node, enum qsmm_mat_e *mat_p)

This function sets *mat_p to the type of a matrix, to which output probability variable var_name of node of multinode model corresponds. If mat_p is 0, then *mat_p will not be set. If node uses a source probability profile provided by another node, then there will be retrieved the type of a matrix of the output probability variable of the latter node.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node or an output probability variable named var_name does not exist.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To calculate the value of an output probability variable, there are calculated relative probabilities of all choice alternatives that include an alternative to which the probability variable corresponds. After that, the relative probabilities are normalized to obtain values of output probability variables that correspond to the choice alternatives. Calculated values are cached.

An output probability variable might correspond to many cells of the state transition matrix of a node. When a type of probability to retrieve for the variable is QSMM_PROB_FQ, then for a relative probability, the sum of frequencies of transitions to target states stored in the cells will be taken. When a type of probability to retrieve is not QSMM_PROB_FQ, then for a relative probability, the weighted mean of output probabilities in those cells will be taken. The weights are the frequencies of transitions to target states stored in the cells. A situation when an output probability variable corresponds to several cells of the state transition matrix is illustrated by the following fragment of an assembler program.

             stt     "s0"
             test    3               ; user instruction with 3 outcomes
             jprob   var0, L2
             stt     "s1"
             ...
     L2:     stt     "s2"

Instruction ‘test 3’ has three outcomes, but in the above fragment of an assembler program they all are processed in the same way. The state transition matrix has rows for all outcomes of the instruction and columns for states ‘s0’, ‘s1’, and ‘s2’. The fragment of the state transition matrix is represented in mat_goto_wmean. There p denotes an output probability, ν denotes a frequency of transition, and pp denotes a profile probability.

To calculate the value of output probability variable var0 of type QSMM_PROB_FQ, relative probabilities ν0,11,12,1 and ν0,21,22,2 are calculated, and ratio 0,21,22,2)/(ν0,11,12,10,21,22,2) is taken for the value. To calculate the value of the variable of a type other than QSMM_PROB_FQ, relative probabilities (p0,1ν0,1+p1,1ν1,1+p2,1ν2,1)/(ν0,11,12,1) and (p0,2ν0,2+p1,2ν1,2+p2,2ν2,2)/(ν0,21,22,2) are calculated, and the second relative probability divided by the sum of both relative probabilities is taken for the value.

A fragment of state transition matrix for calculating the value of a variable

Figure 5.1: a fragment of state transition matrix for calculating the value of a variable

It should be noted that when an output probability variable corresponds to more than one cell of the state transition matrix, calculating output probabilities of types QSMM_PROB_LEARNT and QSMM_PROB_AGGR is performed by a rather bogus algorithm. Therefore, it is recommended to avoid calculating output probabilities of those types or to use output probability variables that correspond to single cells of the state transition matrix. In many cases, output probabilities QSMM_PROB_LEARNT and QSMM_PROB_AGGR can be approximated by output probabilities QSMM_PROB_FQ.

As it was said before, a probability variable used in more than one jprob or case instruction cannot be an output probability variable. The following fragment of an assembler program illustrates this.

     L1:     user    0               ; user instruction
             jprob   var0, L1        ; the first occurrence of variable `var0'
     
     L2:     user    1               ; user instruction
             jprob   var0, L2        ; the second occurrence of variable
                                     ; `var0';
                                     ; encountering this occurrence means that
                                     ; variable `var0' cannot be an output
                                     ; probability variable
             jmp     L1

As it was mentioned above, a probability variable used once, but in an ambiguous context, cannot be an output probability variable too. In the current implementation of the assembler, only jprob instructions can have ambiguous context. The context of a jprob instruction is ambiguous when the instruction is included in blocks of jprob instructions that have different start addresses. Such situation is illustrated by the following fragment of an assembler program.

     L1:     user    0
             jprob   0.5, L3
     
     L2:     jprob   var0, L1
             user    1
     
     L3:     user    2
             jmp     L2

After invoking instruction ‘user 0’, the selection of the next instruction to execute is performed by a block that contains two instructions: ‘jprob 0.5, L3’ and ‘jprob var0, L1’. However, after invoking instruction ‘user 2’, the selection of the next instruction to execute is performed only by the last instruction of that block. In the first case, the block begins with instruction ‘jprob 0.5, L3’, and in the second case the block begins with instruction ‘jprob var0, L1’ (it contains only this instruction). That is, the blocks have different start addresses, a ‘jprob var0, L1’ instruction they contain occurs in the ambiguous context, and, therefore, variable var0 cannot be an output probability variable.

It is recommended to replace fixed profile probability values with output probability variables only in assembler programs for which no warnings are generated when loading them into nodes. Such assembler programs do not contain instructions used in ambiguous contexts. When the QSMM_ASM_VAR_OUT flag is passed to function qsmm_node_asm, all probability variables used only once in such assembler programs become output probability variables.

To get names of output probability variables of a node, the following function can be used.

— Function: int qsmm_enum_var_prob_out (qsmm_t model, int node, qsmm_enum_ent_callback_func_t callback_func, void *paramp)

This function enumerates output probability variables defined in an assembler program loaded into a node of multinode model. If node uses a source probability profile provided by another node, then there will be enumerated output probability variables of the latter node.

The process of enumeration is a repeated calling callback function callback_func, to which entity type QSMM_ENT_VAR_PROB, the name of an output probability variable, and user parameter paramp are passed. See Enumerating Entities, for a description of the type of the callback function.

If the callback function returns a positive value, then the process of enumeration will be continued. If the callback function returns zero, then the process of enumeration will be terminated, and function qsmm_enum_var_prob_out will report success. If the callback function returns a negative value, then the process of enumeration will be terminated, and function qsmm_enum_var_prob_out will report failure.

Function qsmm_enum_var_prob_out does not enumerate output probabilities arrays of the node. An example program that enumerates those arrays for the memory representation of an assembler program is given in Getting Output Probabilities Arrays.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_CALLBACK
The callback function did return an error.

To retrieve the value of an output probability variable, the following function can be used.

— Function: int qsmm_get_node_var_prob_out (qsmm_t model, const char *var_name, int node, enum qsmm_prob_e prob_type, double *valp)

This function sets *valp to a value of output probability variable var_name of node of multinode model. If valp is 0, then *valp will not be set. If valp is not 0, then value *valp retrieved will be a probability of type prob_type. See Generating an Optimal Action, for a description of the elements of enumeration qsmm_prob_e.

If node uses a source probability profile provided by another node, then information on how to calculate the value of probability variable var_name will be obtained from the latter node. See Memory Efficient Cloning a Probability Profile, for a detailed description of this mode.

If valp is not 0, and variable var_name is a controlled or auxiliary probability variable used more than once in an assembler program loaded into node or into a node, which acts as a source of probability profile for node, or used in an ambiguous context in that assembler program, then *valp will be set to -1.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of prob_type is not a valid element of enumeration qsmm_prob_e.
QSMM_ERR_NOTFOUND
A node with identifier node does not exist, or probability variable named var_name is not defined in an assembler program loaded into node or into a node that acts as a source of probability profile for node.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.

Output probabilities, which correspond to case instructions in choice instruction blocks, can be retrieved via output probability variables used in case instructions or as output probabilities arrays referenced by location labels assigned to choice instruction blocks. The contents of output probabilities arrays, which correspond to casels instructions and choice instruction blocks, are fetched by function qsmm_get_node_array_prob_out described in Getting Output Probabilities Arrays.

In QSMM version 1.16, there are introduced functions for fetching aggregate statistics for cycle types that correspond to output probability variables and elements of output probabilities arrays. The statistics are aggregate in the sense that when an output probability variable or an element of output probabilities array correspond to the state transition matrix of a node, the statistics are composed of statistics for cycle types in all matrix cells, which are associated with the variable or the array element. When an output probability variable or an element of output probabilities array correspond to the action emission matrix of a node, aggregate statistics are simply the statistics for a cycle type in a matrix cell associated with the variable or the array element.

To fetch aggregate statistics for cycle types associated with an output probability variable, a function described below can be used. For a description of a function for fetching aggregate statistics for cycle types associated with an element of output probabilities array, see Getting Output Probabilities Arrays.

— Function: int qsmm_get_node_var_prob_cycle (qsmm_t model, const char *var_name, int node, struct qsmm_cycle_s *cycle_p, struct qsmm_cspur_s *cspur_p)

This function fetches aggregate statistics on cycle types that correspond to output probability variable var_name of node of multinode model. If cycle_p is not 0, then *cycle_p will be set to aggregate statistics on the cycle types. If cspur_p is not 0, then *cspur_p will be set to aggregate statistics on spur types for the cycle types. Structures qsmm_cycle_s and qsmm_cspur_s are described in Structures for Accessing Storage.

If cspur_p is not 0 and the output probability variable corresponds to the state transition matrix of the node, then array cspur_p must be capable of holding the number of elements returned for the multinode model by function qsmm_get_nspur. If cspur_p is not 0 and the output probability variable corresponds to the action emission matrix of the node, then array cspur_p must be capable of holding the number of elements returned by function qsmm_get_nspur minus 1. The type of a matrix, to which the output probability variable corresponds, can be obtained using function qsmm_get_node_var_prob_mat described earlier in this subsection.

Aggregate statistics on the cycle types are computed in the following way. Values of fields fq, period_sum_d, and period_sum_c of *cycle_p are the sums of values of those fields in instances of structure qsmm_cycle_s, which correspond to cycle types associated with the output probability variable. The value of cycle_p->profile is computed by calling function qsmm_get_node_var_prob_out and passing QSMM_PROB_PROFILE for a type of probability to retrieve. Values of field delta_sum of elements of array cspur_p are the sums of values of that field of corresponding elements, which contain statistics on spur types for cycle types associated with the output probability variable.

If node uses a source probability profile provided by another node, then information on how to calculate the aggregate statistics will be obtained from the latter node. See Memory Efficient Cloning a Probability Profile, for a detailed description of this mode.

On success, the function returns a non-negative number of cycle types that were used to make up the aggregate statistics. On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node or an output probability variable named var_name does not exist.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.

When retrieving values of particular output probability variables, values of associated output probability variables can be implicitly calculated. All values of output probability variables calculated for a node are stored in a values cache created within the node. The cache is destroyed automatically by functions qsmm_node_call_default (see Transferring Control Between Nodes) and qsmm_node_unload (see Unloading a Probability Profile) invoked for the node, after disassembling the node if it is assembled with the QSMM_ASM_TEMPLATE flag, and when destroying the model instance. For conserving memory, the cache can be destroyed manually by the following function.

— Function: int qsmm_node_var_out_forget (qsmm_t model, int node)

This function destroys the cache of values of output probability variables and arrays of node of multinode model. The cache is used by functions qsmm_get_node_var_prob_out and qsmm_get_node_array_prob_out to speed up fetching output probability variables and arrays of the node.

On success, a non-negative value is returned. If a node with identifier node does not exist, then negative error code QSMM_ERR_NOTFOUND will be returned.


Previous: Output Variables, Up: Using Probability Variables

5.9.4 Auxiliary Variables

Auxiliary probability variables are probability variables, which are neither controlled nor output ones and which are used in assembler programs merely for convenience purposes. Auxiliary probability variables are replaced with their values specified in ‘prob’ directives at the time of loading an assembler program into a node. Information on auxiliary probability variables is not stored in a node, and they cannot be accessed after loading an assembler program into the node.

To load the memory representation of an assembler program, which contains auxiliary probability variables, into a node, you need to pass the QSMM_ASM_VAR_AUX flag to function qsmm_node_asm. The use of a probability variable in an assembler program will not raise an error if the probability variable is the controlled and/or the output one, or if the QSMM_ASM_VAR_AUX flag is passed to function qsmm_node_asm. Note that controlled probability variables must be registered in the instruction class set, and to enable the use of output probability variables, you need to pass the QSMM_ASM_VAR_OUT flag to function qsmm_node_asm.


Next: , Previous: Using Probability Variables, Up: Assembler Programs

5.10 Using Probabilities Lists

Probabilities lists are syntactical constructions that help to reduce clutter in assembler programs by making them shorter and simpler. To a probabilities list used in a casels instruction there may correspond an output probabilities array, with the help of which learned probabilities associated with profile probabilities specified by elements of the probabilities list can be fetched after training a node. Probabilities lists were introduced in QSMM version 1.15.


Next: , Up: Using Probabilities Lists

5.10.1 Defining Probabilities Lists

Probabilities lists are defined in a ‘data’ section of an assembler program.

A definition of probabilities list might contain names of controlled probability variables. Names of such variables can be present in the definition of a probabilities list used in casels instructions, which specify profile probabilities in the state transition matrix, only when the environment state identification engine is represented by a small actor. Names of controlled probability variables can be present in the definition of a probabilities list used in casels instructions, which specify profile probabilities in the action emission matrix, only when the instruction emitting engine is represented by a small actor.

A probabilities list, which contains equal probabilities, can be defined using a ‘probeq’ directive that must be written in one of the following forms:

     ls_name         probeq  length
     ls_name         probeq  length, fixed_prob
     ls_name         probeq  length, var_name

All three forms define a probabilities list named ls_name that contains length elements. The first form defines the probabilities list with all elements equal to 1/(length+1). The second form defines the probabilities list with all elements equal to number fixed_prob, where length*fixed_prob must be less than or equal to 1. Number fixed_prob must be specified either in fixed-point or exponential notation. The third form defines the probabilities list with all elements equal to the value of a probability variable named var_name that must be previously defined using a ‘prob’ directive.

Probability variable var_name might be a controlled or auxiliary probability variable. Expression length*var_name must be less than or equal to 1 for an initial value of probability variable var_name specified in the ‘prob’ directive. If var_name is a controlled probability variable, to which a value is assigned that makes expression length*var_name be greater than 1, then function qsmm_node_var_realize will raise error QSMM_ERR_PSUMGT1 when trying to update profile probabilities, which correspond to a casels instruction that uses probabilities list ls_name.

A probabilities list, which contains explicitly specified probabilities, can be defined using a ‘probls’ directive that has the following syntax:

     ls_name         probls  prob1, prob2, ..., probN

The probabilities list will have name ls_name and contain elements prob1, prob2, ..., probN, where each element can be a number or the name of a probability variable previously defined using a ‘prob’ directive. Numeric elements of probabilities list can be specified either in fixed-point or exponential notation.

The elements list can be splitted into multiple lines. To indicate that a line of elements list is continued on the next line, terminate a line, which is continued, with a comma after the last element on the line. This is shown in the following example:

     ls2             probls  0.04, var1, 0.02, 0.02,
                             0.08, 0.08, var2, var2,
                             var3, 0.06, 0.06, 0.04

The sum of elements of a probabilities list, which contains explicitly specified probabilities, should be less than or equal to 1. If the sum of an element of a probabilities list and elements located earlier in the list exceeds 1, then the element will be discarded when parsing the assembler program. If an element is the name of a probability variable, then for the value of the element there will be taken an initial value of the variable specified in the ‘prob’ directive.

As for the ‘probeq’ directive, probability variables used in arguments of the ‘probls’ directive can be controlled or auxiliary probability variables. If to controlled probability variables, which are used as elements of a probabilities list, there are assigned values that make the sum of all elements of the probabilities list be greater than 1, then function qsmm_node_var_realize will raise error QSMM_ERR_PSUMGT1 when trying to update profile probabilities, which correspond to a casels instruction that references the probabilities list.


Next: , Previous: Defining Probabilities Lists, Up: Using Probabilities Lists

5.10.2 Referencing Probabilities Lists

Probabilities lists are used in casels instructions. See casels Instruction, for more information. For example, a casels instruction, which uses probabilities list ls2 defined in the previous subsection, might look like this:

             casels  ls2,
                     L1, L2, L3, L4, L5, L6,
                     L7, L8, L9, L10, L11, L12

If the QSMM_ASM_TEMPLATE flag is passed to function qsmm_node_asm, then to support including in a disassembled program the values of output probabilities, which correspond to jump labels in arguments of casels instructions, all those instructions encountered in the memory representation of the assembler program will be converted to choice instruction blocks. The exception is that when a casels instruction uses a probabilities list, which contains only one element, the instruction will be converted to a jprob instruction.


Previous: Referencing Probabilities Lists, Up: Using Probabilities Lists

5.10.3 Getting Output Probabilities Arrays

An output probabilities array, which corresponds to a casels instruction or a choice instruction block, is referenced using one of location labels assigned to the instruction or the instruction block. A casels instruction and a choice instruction block with such location labels assigned might look like these:

     LS1:    casels  ls1, L1, L2, L3, L4, L5, L6, L7, L8
     LS2:    choice
             case    0.15, L1
             case    0.05, L2
             case    0.10, L3
             case    0.20, L4
             end     choice

In these examples there are defined two output probabilities arrays: LS1 and LS2. Array elements correspond to jump labels specified in arguments of the casels instruction and to case instructions within the choice instruction block.

Every output probabilities array corresponds either to the state transition matrix or to the action emission matrix of a node. To get the type of a matrix, to which an output probabilities array corresponds, a function described below can be used. The function is introduced in QSMM version 1.16.

— Function: int qsmm_get_node_array_prob_mat (qsmm_t model, const char *label, int node, enum qsmm_mat_e *mat_p)

This function sets *mat_p to the type of a matrix, to which an output probabilities array of node of multinode model corresponds. A location label assigned to a casels instruction or a choice instruction block specifies the array. If mat_p is 0, then *mat_p will not be set. See Output Variables, for a description of the elements of enumeration qsmm_mat_e. If node uses a source probability profile provided by another node, then there will be retrieved the type of a matrix of the output probabilities array of the latter node.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node or output probabilities array label does not exist.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

To get the contents of a segment of output probabilities array, the following function can be used.

— Function: int qsmm_get_node_array_prob_out (qsmm_t model, const char *label, int node, int from, int to, enum qsmm_prob_e prob_type, double *prob_p)

This function fetches the contents of an output probabilities array of node of multinode model. A location label assigned to a casels instruction or a choice instruction block specifies the array. If prob_p is not 0, then output probabilities of type prob_type will be copied from the array to a buffer pointed by prob_p. See Generating an Optimal Action, for a description of the elements of enumeration qsmm_prob_e.

Arguments from and to can be both zero (which is not recommended) or may specify indices of the first element (inclusive) and of the last element (exclusive) of a segment of output probabilities array, the contents of which should be fetched to buffer prob_p. If to is 0, then the length of the output probabilities array will be used as an index (incremented by 1) of the last element of the segment. A special element of the output probabilities array at index equal to the length of the array corresponds to a choice alternative just after the casels instruction or the choice instruction block, to which control is transferred when no jump to one of location labels specified in the casels instruction or case instructions within the choice instruction block is made.

If node uses a source probability profile provided by another node, then information on how to fetch the contents of output probabilities array label will be obtained from the latter node. See Memory Efficient Cloning a Probability Profile, for a detailed description of this mode.

On success, the function returns a non-negative number of elements written to buffer prob_p. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of prob_type is not a valid element of enumeration qsmm_prob_e, or the value of from is negative or is greater than or equal to an index (incremented by 1) of the last element of the segment of the output probabilities array, or the value of to is negative or is greater than the length of the output probabilities array plus 1.
QSMM_ERR_NOTFOUND
A node with identifier node or output probabilities array label does not exist.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.

In QSMM version 1.16 there is introduced a function for fetching aggregate statistics on cycle types associated with an element of output probabilities array. A description of the function is given below. See Output Variables, for a description of the corresponding function for fetching aggregate statistics on cycle types associated with an output probability variable and for additional information on the concept of aggregate statistics.

— Function: int qsmm_get_node_array_prob_cycle (qsmm_t model, const char *label, int node, int offs, struct qsmm_cycle_s *cycle_p, struct qsmm_cspur_s *cspur_p)

This function fetches aggregate statistics on cycle types that correspond to an element of an output probabilities array of node of multinode model. A location label assigned to a casels instruction or a choice instruction block specifies the array.

The index of the element of the array is specified by offs. A special element of the array at index equal to the length of the array corresponds to a choice alternative just after the casels instruction or the choice instruction block, to which control is transferred when no jump to one of location labels specified in the casels instruction or case instructions within the choice instruction block is made.

If cycle_p is not 0, then *cycle_p will be set to aggregate statistics on the cycle types. If cspur_p is not 0, then *cspur_p will be set to aggregate statistics on spur types for the cycle types. Structures qsmm_cycle_s and qsmm_cspur_s are described in Structures for Accessing Storage.

If cspur_p is not 0 and the output probabilities array corresponds to the state transition matrix of the node, then array cspur_p must be capable of holding the number of elements returned for the multinode model by function qsmm_get_nspur. If cspur_p is not 0 and the output probabilities array corresponds to the action emission matrix of the node, then array cspur_p must be capable of holding the number of elements returned by function qsmm_get_nspur minus 1. The type of a matrix, to which the output probabilities array corresponds, can be obtained using function qsmm_get_node_array_prob_mat described earlier in this subsection.

Aggregate statistics on the cycle types is computed in the following way. Values of fields fq, period_sum_d, and period_sum_c of *cycle_p are the sums of values of those fields in instances of structure qsmm_cycle_s, which correspond to cycle types associated with the element of output probabilities array. The value of cycle_p->profile is computed by calling function qsmm_get_node_array_prob_out for the element of output probabilities array and passing QSMM_PROB_PROFILE for a type of probability to retrieve. Values of field delta_sum of elements of array cspur_p are the sums of values of that field of corresponding elements, which contain statistics on spur types for cycle types associated with the element of output probabilities array.

If node uses a source probability profile provided by another node, then information on how to calculate the aggregate statistics will be obtained from the latter node. See Memory Efficient Cloning a Probability Profile, for a detailed description of this mode.

On success, the function returns a non-negative number of cycle types that were used to make up the aggregate statistics. On failure, the function returns a negative error code. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of offs is negative or is greater than the length of the output probabilities array.
QSMM_ERR_NOTFOUND
A node with identifier node or output probabilities array label does not exist.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.

When retrieving the contents of a segment of output probabilities array of a node, explicitly and implicitly calculated probability values are stored in the values cache created within the node. In certain situations, the cache is destroyed automatically, but it can also be destroyed manually by function qsmm_node_var_out_forget. See Output Variables, for more information on the cache of values of output probability variables and arrays of node.

If there is a need to dump the contents of all output probabilities arrays defined in an assembler program, then only instructions of types QSMM_INSTR_CHOICE and QSMM_INSTR_CASELS must be considered. The type of an instruction specified by its handle can be obtained by function qsmm_get_instr_type. If the instruction has at least one location label assigned, then there will be an output probabilities array associated with that instruction. The first location label, which identifies the output probabilities array, can be retrieved by call qsmm_get_instr_label(instr,0), where instr is an instruction handle. When 0 is returned, the instruction does not have location labels assigned.

For an instruction of type QSMM_INSTR_CHOICE, the length of an output probabilities array associated with the instruction is equal to a value returned by function qsmm_get_instr_nnested for that instruction minus 1 (the last nested instruction is the ‘end choice’ instruction). For an instruction of type QSMM_INSTR_CASELS, the length of the output probabilities array is equal to the length of probabilities list returned by function qsmm_get_prg_ls_nprob. The name of probabilities list, which should be passed to that function, can be retrieved by function qsmm_get_instr_ls_name for the instruction handle.

An example function that dumps the contents of all output probabilities arrays of a node into which assembler program prg was loaded is given below. The function returns 0 on success or -1 on out of memory error.

     #include <stdlib.h>
     #include <qsmm/qsmm.h>
     
     static int dump_prob_out_arrays(qsmm_t qsmm,
                                     int node,
                                     qsmm_prg_t prg,
                                     enum qsmm_prob_e prob_type) {
         int ii, jj, ninstr, prob_allo=1, result=-1;
         double *probp=calloc(prob_allo,sizeof(*probp));
         if (!probp) goto Exit;
         ninstr=qsmm_get_prg_ninstr(prg);
         for (ii=0; ii<ninstr; ii++) {
             const char *label;
             int nprob;
             qsmm_instr_t instr=qsmm_get_prg_instr(prg,ii);
             if (!(label=qsmm_get_instr_label(instr,0))) continue;
             switch (qsmm_get_instr_type(instr)) {
                 case QSMM_INSTR_CHOICE:
                     nprob=qsmm_get_instr_nnested(instr)-1;
                     break;
                 case QSMM_INSTR_CASELS:
                     nprob=qsmm_get_prg_ls_nprob(
                               prg, qsmm_get_instr_ls_name(instr));
                     break;
                 default:
                     continue;
             }
             if (prob_allo<nprob) {
                 double *newp;
                 if (!(newp=realloc(probp,nprob*sizeof(*newp)))) goto Exit;
                 prob_allo=nprob;
                 probp=newp;
             }
             qsmm_get_node_array_prob_out(qsmm, label, node, 0, nprob,
                                          prob_type, probp);
             for (jj=0; jj<nprob; jj++)
                 printf("%s[%d]=%.15E\n",label,jj,probp[jj]);
         }
         result=0;
     
     Exit:
         if (probp) free(probp);
         return result;
     }


Next: , Previous: Using Probabilities Lists, Up: Assembler Programs

5.11 Cloning a Probability Profile

When there is a need to load the same probability profile into multiple nodes, an assembler program can be loaded into one node, and then the probability profile can be copied to other nodes. In QSMM, the process of copying a probability profile from a node to another one is called cloning a probability profile. Cloning a probability profile from a node to other nodes is faster than loading the same assembler program into them.

To clone a probability profile, the following function can be used.

— Function: int qsmm_node_profile_clone (qsmm_t model, int node_from, int node_to, unsigned int flags)

This function copies a probability profile from node node_from of multinode model to node node_to of the model. The function clears statistics on the event history that might have been collected for node node_to. If node node_to already has a probability profile loaded, then that profile along with additional information, which might be copied by this function (see a description of argument flags below), will be first unloaded. To clear statistics on the event history and to unload the profile, function qsmm_node_unload described in Unloading a Probability Profile is implicitly called. Probability profiles manually written to storage without prior calling function qsmm_node_asm cannot be copied by function qsmm_node_profile_clone.

Argument flags is a bitmask, which specifies types of additional information that should be copied from node node_from to node node_to along with the probability profile. Bits of the bitmask, which are taken into account, are represented by the following macros that can be merged by bitwise “or.”

— Macro: QSMM_NODE_CLONE_VARS

Copy definitions of controlled and output probability variables and arrays, so values of controlled probability variables of node node_to can be set, and values of output probability variables and arrays of that node can be retrieved, as they can be set and retrieved for node node_from. Before copying the definitions of controlled probability variables, function qsmm_node_var_realize is implicitly called for node node_from if that node has uncommitted assignments to those variables.

— Macro: QSMM_NODE_CLONE_STATE_NAMES

Copy names assigned to node states by arguments of stt instructions. You will be able to retrieve that information for node node_to by functions qsmm_get_node_state_name and qsmm_get_node_state_by_name and use that information when disassembling node node_to and dumping the state transition matrix and the action emission matrix of node node_to.

— Macro: QSMM_NODE_CLONE_TEMPLATE

If node node_from has an assembler program template associated with it, then copy that template. The template will be used when disassembling node node_to.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of node_from is equal to the value of node_to.
QSMM_ERR_NOTFOUND
A node with identifier node_from or node_to does not exist.
QSMM_ERR_NOSTATE
A node with identifier node_to has the number of states less than it is required to hold the probability profile of a node with identifier node_from.
QSMM_ERR_NOSAMENC
Instruction class sets that represent node classes, to which nodes node_from and node_to belong, are not the same.
QSMM_ERR_NOPROF
A probability profile is not loaded into a node with identifier node_from.
QSMM_ERR_PROFSRCU
A node with identifier node_from is a user of a source probability profile provided by another node.
QSMM_ERR_PROFSRCP
One of the following conditions is met:
  • a node with identifier node_to is a source of probability profile for other nodes;
  • function qsmm_node_var_realize was implicitly called, and it turned out that a node with identifier node_from is a source of probability profile for other nodes.

QSMM_ERR_PSUMGT1
Function qsmm_node_var_realize was implicitly called for node node_from, and it turned out that the sum of probabilities of case instructions in a choice instruction block or the sum of elements of a probabilities list used by a casels instruction will exceed 1 if assignments to controlled probability variables are committed. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.


Next: , Previous: Cloning a Probability Profile, Up: Assembler Programs

5.12 Memory Efficient Cloning a Probability Profile

The QSMM framework supports a mechanism of deferred copying a probability profile from one node to another node when no memory is consumed during the copying operation itself. The deferred copying operation only sets a correspondence between two nodes, where one node becomes a source of a probability profile and another node becomes a user of that probability profile. A node can be a source of probability profile for multiple other nodes. Applying deferred copying operation makes sense when the memory is allocated dynamically, i.e. when the multinode model uses map storage for holding statistics collected over the event history. The longer gets the event history the same efficiency of memory consumption is provided in case when there is no probability profile loaded into a node and in case when the node is a user of a source probability profile provided by another node.

The deferred copying mechanism works in the following way. Information on a probability profile is stored in instances of structures qsmm_state_s and qsmm_cycle_s as described in Structures for Accessing Storage. Statistics collected over the event history is also stored in those instances. However, initially there is no statistics collected for a node, which acts as a user of probability profile6, so the default statistics can be used when needed. Information on a probability profile for that node can be obtained using redirection, i.e. taken from another part of storage that corresponds to a node, which serves as a source of probability profile. When the event history gets longer, and there is a need to update statistics for a particular action choice state or cycle type, the corresponding instance of structure qsmm_state_s or qsmm_cycle_s is allocated in storage. Information on a probability profile is copied there from another part of storage, and updated statistics is written there. Later, the information on a probability profile will be read from the allocated instance directly.

Values of controlled probability variables defined in an assembler program loaded into a node that serves as a source of a probability profile can be changed for a node, which acts as a user of that probability profile, in the ordinary way. Information on how elements of the state transition matrix and the action emission matrix of the latter node should be updated when changing the values of controlled probability variables for that node is taken from the former node. Changing values of probability variables causes allocation of corresponding instances of structures qsmm_state_s and qsmm_cycle_s in storage for the latter node if they are not allocated yet.

The following information stored in a node, which serves as a source of a probability profile, becomes automatically available for a node, which acts as a user of that probability profile.

The following operations cannot be performed on a node that serves as a source of probability profile for other nodes:

The following operations cannot be performed on a node that acts as a user of a probability profile provided by another node:

When dumping the state transition matrix or the action emission matrix, or when disassembling a node, which is a user of a probability profile provided by another node, keep in mind that not all probabilities of type QSMM_PROB_PROFILE may yet be copied from a node, which serves as a source of the probability profile. Because the collection of probabilities can be incomplete, calculating probabilities of that type for a node, which is a user of probability profile, is not recommended.

When the deferred copying mechanism is used for some nodes of a multinode model, this slows down all access operations to storage, which is used by the model, approximately twice (even for other nodes of the model).

To set a correspondence between two nodes of a multinode model, where one node becomes the source of a probability profile and another node becomes a user of that probability profile, the following function can be used.

— Function: int qsmm_set_node_profile_source (qsmm_t model, int node_from, int node_to, int rez1)

This function sets node node_from of multinode model as the source of a probability profile and sets node node_to of the model as a user of that probability profile. Argument rez1 is reserved for future use and must be equal to 0.

The function clears statistics on the event history that might have been collected for node node_to. If node node_to has a probability profile loaded, then that profile along with information, which depends on the profile, will be unloaded. To clear statistics on the event history and to unload the profile, function qsmm_node_unload described in the next section is implicitly called.

If node node_from has uncommitted assignments to controlled probability variables, then function qsmm_node_var_realize will be implicitly called for that node.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of node_from is equal to the value of node_to.
QSMM_ERR_NOTFOUND
A node with identifier node_from or node_to does not exist.
QSMM_ERR_NOSTATE
A node with identifier node_to has the number of states less than it is required to hold the probability profile of a node with identifier node_from.
QSMM_ERR_NOSAMENC
Instruction class sets that represent node classes, to which nodes node_from and node_to belong, are not the same.
QSMM_ERR_NOPROF
A probability profile is not loaded into a node with identifier node_from.
QSMM_ERR_PROFSRCU
A node with identifier node_from is a user of a source probability profile provided by another node.
QSMM_ERR_PROFSRCP
One of the following conditions is met:
  • a node with identifier node_to is a source of probability profile for other nodes;
  • function qsmm_node_var_realize was implicitly called, and it turned out that a node with identifier node_from is already a source of probability profile for other nodes.

QSMM_ERR_PSUMGT1
Function qsmm_node_var_realize was implicitly called for node node_from, and it turned out that the sum of probabilities of case instructions in a choice instruction block or the sum of elements of a probabilities list used by a casels instruction will exceed 1 if assignments to controlled probability variables are committed. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_UNSUPPLA
The multinode model has positive length of the look-ahead signal segment.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.

A correspondence between a node, which is the source of a probability profile, and a node, which is a user of that probability profile, can be broken off by function qsmm_node_unload described in the next section.


Next: , Previous: Memory Efficient Cloning a Probability Profile, Up: Assembler Programs

5.13 Unloading a Probability Profile

To unload statistics and a probability profile from a node, the following function can be used.

— Function: int qsmm_node_unload (qsmm_t model, int node)

This function performs the following operations on node of multinode model:

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_NOTFOUND
A node with identifier node does not exist.
QSMM_ERR_PROFSRCP
A node with identifier node is a source of probability profile for other nodes.
QSMM_ERR_UNTIMELY
The model instance does not exist.
QSMM_ERR_STORAGE
Statistics storage failure. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation. This could leave the model instance in indeterminate state. If the model instance is in indeterminate state, then after removing a reason of the error, the operation can be repeated, and if the function succeeds, then the model instance state will become determinate.


Next: , Previous: Unloading a Probability Profile, Up: Assembler Programs

5.14 Using the Assembler Preprocessor

The assembler preprocessor, which is part of the QSMM framework, was primarily developed to provide the means of encapsulating similar blocks of assembler source code in macros that can be reused in an assembler program. Macros can define nested control transfer structures and produce unique location labels when expanded. The preprocessor also provides the means of including other files in an assembler source file.

The assembler preprocessor returns a single output buffer with a text of preprocessed program. The contents of all included source files are copied explicitly to the preprocessed output. The preprocessed output might contain ‘line’ directives that describe source locations, which could be indicated in error, warning, and note messages generated by the assembler. The preprocessor also merges string literals on a line, which go one after another and are delimited by zero or more whitespace characters, into a single string literal.

When performing transformations of the source text, the assembler preprocessor may break alignment of multiline comments. This may prevent proper detection by the assembler whether a multiline comment is continued on the next line, and comments may be assigned to some instructions incorrectly. At present, this cannot be generally avoided.

The assembler preprocessor provides only basic features necessary to work with symbols and macros. If you need more sophisticated features to produce the source text of an assembler program, then either generate input to the preprocessor using an auxiliary function, program, or a script, or create an assembler program, which does not require preprocessing, by those means directly. Example C programs that generate input to the assembler preprocessor, which is then converted to an assembler program and loaded into a node, are described in asmat, tohuff-test, and predict-test.


Next: , Up: Using the Assembler Preprocessor

5.14.1 Changing Line Number and File Name

To change current line number and possibly the name of the current source file being preprocessed, which are tracked by the assembler preprocessor, with emitting a corresponding ‘line’ directive to the preprocessed output for subsequent interpreting by the assembler, use the ‘line’ directive of the assembler preprocessor. Additionally, information changed by the ‘line’ directive is used when generating error and note messages by the assembler preprocessor.

The directive must be placed on a separate line (after one or more whitespace characters at the beginning of the line) and must be in one of the following formats:

             line    line_number
             line    line_number, file_name

In the first case, the directive changes the number of the next line in the current source file to line_number. In the second case, the directive changes the number of the next line to line_number and the name of current source file to file_name. The value of file_name must be a (quoted) string literal.


Next: , Previous: Changing Line Number and File Name, Up: Using the Assembler Preprocessor

5.14.2 Including Other Source Files

To include another source file in an assembler source file, use the ‘include’ directive:

             include string_literal

Argument string_literal specifies a (quoted) full or relative path to an included file. A relative path is interpreted by using for the base directory a directory that contains a file with the ‘include’ directive, a base directory specified as a parameter of preprocessing if the name of that file is unknown, or current working directory if the base directory is not specified as a parameter of preprocessing. The directive must be written on a separate line after one or more whitespace characters. The directive must not occur within a macro definition. The maximum nesting level of ‘include’ directives is equal to 100.


Next: , Previous: Including Other Source Files, Up: Using the Assembler Preprocessor

5.14.3 Defining Symbols

To define or redefine a symbol, use the ‘def’ directive:

     name    def     value

This statement defines or redefines symbol name to value. A symbol name must start at the beginning of a line. A symbol value must not contain commas not within string literals.

There are two possible scopes of a symbol: global and local. Global symbols are those defined not within macros. If the ‘def’ directive within a macro is used for symbol name, which has been defined as a global one, then that global symbol will be redefined to a new value.

Local symbols are those defined within macros. Macro arguments are also got implicitly defined as local symbols. Local symbols are not visible outside of macros where they were defined. If the ‘def’ directive within a macro is used for symbol name, which has been defined as a local one, then that local symbol will be redefined to a new value.

If the name of a symbol occurs as a token in the text in the scope of that symbol, then that token will be replaced with a symbol value. If the symbol value has to be concatenated with adjacent tokens, then prepend and/or append characters ‘##’ to the symbol name token.

For example, source text

     st      def     01
     
             choice
             case    a##st##_00, l##st##_00
             case    a##st##_01, l##st##_01
             end     choice
     
             jmp     l##st##_02

will be preprocessed to text

             choice
             case    a01_00, l01_00
             case    a01_01, l01_01
             end     choice
     
             jmp     l01_02

When a symbol value is a verbatim text, to convert the symbol value to a string literal, prepend character ‘#’ to the symbol name token. For example, source text

     id       def     5
     
     s##id:   stt     #id

will be preprocessed to text

     s5:   stt     "5"

Names of symbols may occur in a value to which a symbol is defined or redefined. After expanding those symbols, the value of the symbol must reduce either to verbatim text, which does not contain commas, or to a string literal.

The assembler preprocessor supports predefined symbol ‘__UNIQUE__’, which is expanded to verbatim text in the form of the next element from the sequence of natural numbers. That symbol can be used to generate unique location labels in macros.


Next: , Previous: Defining Symbols, Up: Using the Assembler Preprocessor

5.14.4 Defining Macros

A macro definition looks like this:

     name    macro   arg1, arg2, ...
             text
             end     macro

The above block of code defines macro name with arguments arg1, arg2, ... A macro may have no arguments at all. Names of macro arguments work as names of local symbols that can be used within a macro.

If a macro has a long list of arguments, then the list can be splitted into multiple lines. To indicate that a line of arguments list is continued on the next line, terminate a line, which is continued, with a comma after the name of an argument. This is shown in the following example:

     mac1    macro   arg1, arg2, arg3, arg4,
                     arg5, arg6, arg7, arg8
             ...
             end     macro

Below there is given an example of a macro that can represent a state linked to another state in a chain of state transitions:

     g1      macro   id, ll
     
     s##id:  stt     #id
             regst                   ; register passing the state
             jmp     ll
     
             end     macro

Macro definitions cannot be nested. However, a macro can expand another macro, which expands another macro, and so on. The maximum supported nesting level of macro expansions is equal to 65535.

To expand macro name defined earlier in source text and use arg1, arg2, ... for values of macro arguments, write a line like this:

             name    arg1, arg2, ...

The name of expanded macro must be written on a line after one or more whitespace characters. The name can be prepended with a location label definition. No substitutions of symbol names with symbol values are performed in names of macros to expand. However, the substitutions are performed in values of macro arguments. After substituting, the value of every macro argument must reduce either to verbatim text, which does not contain commas, or to a string literal.

If a macro is expanded using a long list of arguments, then the list can be splitted into multiple lines. To indicate that a line of arguments list is continued on the next line, terminate a line, which is continued, with a comma after the value of an argument. This is shown in the following example:

             mac1    "alpha", 0.1, "beta",  0.2,
                     "gamma", 0.3, "delta", 0.4


Next: , Previous: Defining Macros, Up: Using the Assembler Preprocessor

5.14.5 Generating Unique Location Labels

Macros should often contain definitions of location labels that must not duplicate when a macro is expanded several times in the source text. When there is a number of location labels used locally in a macro, local symbols can be defined for them using lines of code like these:

     lu0     def     u##__UNIQUE__
     lu1     def     u##__UNIQUE__
     lu2     def     u##__UNIQUE__

The above directives define symbols lu0, lu1, and lu2, values of which have form ui, where i is an increasing integer number. When the macro is expanded the first time, symbols lu0, lu1, and lu2 take values u1, u2, u3 (if there are no other symbols defined using predefined symbol ‘__UNIQUE__’). When the macro is expanded the second time, symbols lu0, lu1, and lu2 take values u4, u5, u6, and so on. Thus, values of symbols lu0, lu1, and lu2 will never duplicate, and those symbols can be used for location labels within macros that are expanded several times. This is illustrated by the following example of a macro:

             .data
     
     ls2     probeq  2
     
             .code
     
     emit_ch macro
     
     lu0     def     u##__UNIQUE__
     lu1     def     u##__UNIQUE__
     lu2     def     u##__UNIQUE__
     
             casels  ls2, lu0, lu1
             emit    "A"
             jmp     lu2
     
     lu0:    emit    "B"
             jmp     lu2
     
     lu1:    emit    "C"
     
     lu2:
             end     macro

This macro emits character ‘A’, ‘B’, or ‘C’ by corresponding user instruction emit. The macro can be safely expanded an arbitrary number of times, and this should not cause problems with duplicate location labels. For example, to emit a sequence of two characters, expand the macro twice:

             ...
             emit_ch
             emit_ch
             ...


Next: , Previous: Generating Unique Location Labels, Up: Using the Assembler Preprocessor

5.14.6 Specifying State Transition Networks

A researcher may want to investigate the behavior of a state transition network that operates under the control of QSMM. The network can be defined by an assembler program that contains a number of macro definitions. Every expanded macro might correspond to a state, from which there can be transitions to other states. The text of macro g1 that can represent a state linked to another state in a chain of state transitions was already given in Defining Macros. Along with instruction regst that macro might contain other user instructions, which perform custom operations upon entering a state.

Below there is given the text of macro g2 that defines a state with two possible transitions to states specified by arguments of the macro. To every transition a probability corresponds, according to which user spur increment is made. The probabilities are specified by arguments of the macro. User instruction incspr increments user spur by the logarithm of a probability specified as an instruction argument.

     g2      macro   id, l1,p1, l2,p2
     lu      def     u##__UNIQUE__
     
     s##id:  stt     #id
             regst
             jprob   0.5, lu
             incspr  p2
             jmp     l2
     
     lu:     incspr  p1
             jmp     l1
     
             end     macro

Below there is represented an example state transition network. The initial state is encircled by the thick line.

An example state transition network

Figure 5.2: an example state transition network

The state transition network can be described by the following assembler program fragment located after definitions of macros g1 and g2.

             g2      0, s1, 0.5,  s5, 0.5
     
             g2      1, s2, 0.5,  s3, 0.5
             g1      2, s4
             g1      3, s4
             g2      4, s1, 0.5,  s9, 0.5
     
             g2      5, s6, 0.9,  s7, 0.1
             g1      6, s8
             g1      7, s8
             g2      8, s5, 0.5,  s9, 0.5
     
             g1      9, s0

Note that when a state is connected to just one other state, the state transition is performed deterministically. When the environment state identification engine is represented by a small actor, the use of a state transition network with states connected to several different numbers (greater than 1) of other states will imply relying on a rather weak concept of the mean number of actor's output signals (described in Other Parameters of an Actor) that biuniquely correspond to states. Therefore, it is recommended either to use a state transition network with states, where each state is connected to just one other state and/or to certain fixed number of other states, or to use a large actor for the environment state identification engine that does not have such deficiency. In the latter case, the number of output signals of a small actor associated with the large actor is fixed and is equal to the arity of Huffman tree. The same is pertinent to invocation of instructions in action choice states: if in different action choice states there can be invoked instructions from the sets of user and mixed type instructions of different sizes greater than 1, it is recommended to use the instruction emitting engine represented by a large actor.

Another peculiarity a researcher should keep in mind is that the auto-increment of the automatic spur is not well-defined when the state transition network has transitions performed deterministically. Such transitions can appear not only between principal states of the network, but also between user and mixed type instructions within definitions of those states. Moreover, it is often useful to increment the automatic spur only during transitions between a subset of states defined by an assembler program. Therefore, it is generally recommended switching off the auto-increment of the automatic spur for assembler programs, especially when they define deterministically performed state transitions. In this case, automatic spur (or spur of other kind used instead of it) can be incremented manually by a well-defined algorithm supplied by the researcher during invocation of user instructions from an instruction set provided by the researcher.


Previous: Specifying State Transition Networks, Up: Using the Assembler Preprocessor

5.14.7 Getting a Preprocessed Output

Functions qsmm_parse_asm_source_* described in Parsing an Assembler Program can automatically call the assembler preprocessor to preprocess a source program text if the QSMM_PARSE_ASM_PREPROCESS flag is passed to those functions. However, sometimes it might be necessary to obtain a preprocessed source text directly. To accomplish this task, the following functions can be used.

— Function: int qsmm_preprocess_asm_source_buf (const char *in_p, const char *cwd_p, int rez1, void *rez2, void *rez3, qsmm_msglist_t msglist, char **out_pp)
— Function: int qsmm_preprocess_asm_source_stream (FILE *filep, const char *cwd_p, int rez1, void *rez2, void *rez3, qsmm_msglist_t msglist, char **out_pp)

Function qsmm_preprocess_asm_source_buf preprocesses a source program text, which is provided using argument in_p in the form of a NULL-terminated string. Function qsmm_preprocess_asm_source_stream preprocesses a source program text, which the function reads from stream filep.

On success, the functions set *out_pp to a pointer to an allocated NULL-terminated string that contains the preprocessed output. If the preprocessor produces the zero-length output, then *out_pp will be set to 0. If out_pp is 0, then *out_pp will not be set. If after successful function completion *out_pp is not 0, then a memory block pointed by *out_pp should be freed by function free when its contents are not needed any longer.

If msglist is not 0, then messages generated during preprocessing will be written to message list msglist. If cwd_p is not 0, then cwd_p will be used for the name of current working directory when resolving ‘include’ directives. If cwd_p is 0, then the actual current working directory will be used when resolving ‘include’ directives. Arguments rez1, rez2, and rez3 are reserved for future use and must be equal to 0.

On success, the functions return a non-negative number of bytes in the preprocessed output or constant INT_MAX. If out_pp is not 0 and that number is greater than 0 (and less than INT_MAX), then it will be the number of bytes in a string pointed by *out_pp, not counting trailing byte 0. When the functions return INT_MAX, the number of bytes in a string pointed by *out_pp (if out_pp is not 0) is greater than or equal to INT_MAX. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_PRG
One or more errors were encountered in the source program text.
QSMM_ERR_ILSEQ
The source program text cannot be converted to a wide string according to the current locale or the preprocessed output cannot be converted to a multibyte string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.

— Function: int qsmm_preprocess_asm_source_file (const char *fln, int rez1, void *rez2, void *rez3, qsmm_msglist_t msglist, char **out_pp)

This function preprocesses a source program text provided in a file named fln. On success, the function sets *out_pp to a pointer to an allocated NULL-terminated string that contains the preprocessed output. If the preprocessor produces the zero-length output, then *out_pp will be set to 0. If out_pp is 0, then *out_pp will not be set. If after successful function completion *out_pp is not 0, then a memory block pointed by *out_pp should be freed by function free when its contents are not needed any longer.

If msglist is not 0, then messages generated during preprocessing will be written to message list msglist. Arguments rez1, rez2, and rez3 are reserved for future use and must be equal to 0.

On success, the function returns a non-negative number of bytes in the preprocessed output or constant INT_MAX. If out_pp is not 0 and that number is greater than 0 (and less than INT_MAX), then it will be the number of bytes in a string pointed by *out_pp, not counting trailing byte 0. When the function returns INT_MAX, the number of bytes in a string pointed by *out_pp (if out_pp is not 0) is greater than or equal to INT_MAX. On failure, a negative error code is returned. Currently, the following error codes can be returned.

QSMM_ERR_LIBC
When accessing file fln, the operating system has returned an error. Variable errno holds the error code.
QSMM_ERR_PRG
One or more errors were encountered in the source program text.
QSMM_ERR_ILSEQ
The source program text cannot be converted to a wide string according to the current locale or the preprocessed output cannot be converted to a multibyte string according to the current locale.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Previous: Using the Assembler Preprocessor, Up: Assembler Programs

5.15 Example of Working with an Assembler Program

In the example of working with an assembler program, an agent controlled by the sample program has to find a path to the gold in a labyrinth and then to an exit from the labyrinth. As in sample program samples/labyr2.c described in Example of Using the Actor API, the image of the labyrinth is encoded in the C source program text using a subset of ASCII characters that look like pseudographics. You can change the image to test the behavior of the agent for various configurations of the labyrinth.

To learn the configuration of the labyrinth, the agent visits the labyrinth the number of times defined by macro NVISIT. A visit is considered finished when the agent moves to a cell where the labyrinth exit is located.

Each labyrinth cell is a rectangle 4x3. Maximum zero-based indices of a column and a row of labyrinth cell are defined by macros MAX_X and MAX_Y. Coordinates of the entry cell of the labyrinth relative to its upper left corner are defined by macros ENTRY_X and ENTRY_Y. Characters ‘*’ at cell corners denote a cell with the gold. Characters ‘#’ at cell corners denote a labyrinth exit.

Space characters denote allowed movement paths, and other characters denote places to which movement is impossible. If a cell is reachable from the labyrinth entry, then it must have two spaces in the middle. Every reachable cell must also have two spaces or two non-spaces at the top and at the bottom (in the middle), which indicates whether movement is allowed to an adjacent cell located at the north and/or the south. When modifying the image of the labyrinth, make sure that cells are properly aligned and connected, and there are no paths outside of the labyrinth, otherwise an assertion failure will occur.

Although the sample program uses a picture of labyrinth encoded in its source text, in other respects this program works much like as sample program samples/maze.c described in Example of Working in Large-scale Mode. That is, in a default mode of program operation, the agent does not know its precise location in the labyrinth and receives only limited amount of information about current location. However, as opposed to sample program samples/maze.c, an agent controlled by the program described in this section receives either an indication whether current cell is an exit from the labyrinth or, if the current cell is not an exit from the labyrinth, a union of the following pieces of information:

In the default mode of program operation, the number of tracked environment states is defined by macro NSTATE. This number is about 3.5 times greater than in sample program samples/maze.c, which along with much greater amount of information about current condition of visiting the labyrinth received by the agent considerably decreases the probability of infinite looping during program execution. Otherwise, such infinite loopings would occur once in a while for some reason.

After its invocation, the sample program prints the number of tracked environment states. Then, after every visit of the labyrinth, the sample program prints an index number of the visit, the number of times the agent found the gold since the beginning of program execution, the length of all traversed visiting paths, and the average amount of gold found per one move in the labyrinth. In the default mode of program operation for the default configuration of the labyrinth, after starting the program, the agent visits the labyrinth a number of times without taking the gold, until at some point the agent usually “understands” how to take the gold, and then it finds the gold at almost every visit of the labyrinth.

At the last visit of the labyrinth, the sample program switches the terminal to the full-screen mode and shows the picture of the labyrinth. After a user presses <SPACE>, the sample program shows movements of the agent in the labyrinth. An indication whether the agent took the gold and current length of visiting path are printed. After the agent finds an exit from the labyrinth, the user can press <Q> to turn off the full-screen terminal mode and quit the sample program with dumping to file prg_disasm in the current directory a learned assembler program according to which the agent operates.

A random seed can be specified as the first program argument. If the random seed is non-negative, then the agent will operate normally. If the random seed is negative, then the agent will move in the labyrinth completely randomly. You could compare the program output for these two modes of program execution.

A special mode of operation of the sample program, as opposed to the default mode, is the use of a profile assembler program that helps the agent to do its job more efficiently. The name of a file with the profile assembler program should be specified as the second program argument. A single node of the model is assembled with the QSMM_ASM_TEMPLATE flag set, so when disassembling the node at the end of execution of the sample program, a disassembled program will be the input assembler program, in which profile probabilities are replaced with learned probabilities.

The source text of the profile assembler program is provided in file samples/prg_maze in the package distribution and is also given below. That assembler program is intended for use with labyrinths that have up to 10 cells in width and height. It actually provides tracking a precise location of the agent in the labyrinth, which is why the agent will find the gold at almost all visits of the labyrinth. The final visiting path shown in the full-screen terminal mode at the end of execution of the sample program does not have vacillations that take place when the profile assembler program is not used.

             .data
     
     ls_a    probeq  3
     
             .code
     
     
     reloc   macro   l_repeat, new_row, new_col
     
     l_g0    def     g0_r##new_row##_c##new_col
     l_g1    def     g1_r##new_row##_c##new_col
     
             joe     0, l_g0
             joe     1, l_g0
             joe     2, l_g0
             joe     3, l_g0
             joe     4, l_g0
             joe     5, l_g0
             joe     6, l_g0
             joe     7, l_g0
             joe     8, l_g0
             joe     9, l_g0
             joe     10, l_g0
             joe     11, l_g0
             joe     12, l_g0
             joe     13, l_g0
             joe     14, l_g0
             joe     15, l_g0
             joe     32, l_g1
             joe     33, l_g1
             joe     34, l_g1
             joe     35, l_g1
             joe     36, l_g1
             joe     37, l_g1
             joe     38, l_g1
             joe     39, l_g1
             joe     40, l_g1
             joe     41, l_g1
             joe     42, l_g1
             joe     43, l_g1
             joe     44, l_g1
             joe     45, l_g1
             joe     46, l_g1
             joe     47, l_g1
             jmp     l_repeat
     
             end     macro
     
     
     state   macro   is_gf, row, col,
                     row_north, row_south, col_west, col_east
     
     repeat  def     u##__UNIQUE__
     m_north def     u##__UNIQUE__
     m_east  def     u##__UNIQUE__
     m_south def     u##__UNIQUE__
     
     g##is_gf##_r##row##_c##col:
     repeat: stt
     
             casels  ls_a,
                     m_north,
                     m_east,
                     m_south
     
             mv      west
             reloc   repeat, row, col_west
     
     m_south:
             mv      south
             reloc   repeat, row_south, col
     
     m_east: mv      east
             reloc   repeat, row, col_east
     
     m_north:
             mv      north
             reloc   repeat, row_north, col
     
             end     macro
     
     
     strow   macro   is_gf, row, row_north, row_south
     
             state   is_gf, row, 0, row_north, row_south, 9, 1
             state   is_gf, row, 1, row_north, row_south, 0, 2
             state   is_gf, row, 2, row_north, row_south, 1, 3
             state   is_gf, row, 3, row_north, row_south, 2, 4
             state   is_gf, row, 4, row_north, row_south, 3, 5
             state   is_gf, row, 5, row_north, row_south, 4, 6
             state   is_gf, row, 6, row_north, row_south, 5, 7
             state   is_gf, row, 7, row_north, row_south, 6, 8
             state   is_gf, row, 8, row_north, row_south, 7, 9
             state   is_gf, row, 9, row_north, row_south, 8, 0
     
             end     macro
     
     
     strows  macro   is_gf
     
             strow   is_gf, 0, 9, 1
             strow   is_gf, 1, 0, 2
             strow   is_gf, 2, 1, 3
             strow   is_gf, 3, 2, 4
             strow   is_gf, 4, 3, 5
             strow   is_gf, 5, 4, 6
             strow   is_gf, 6, 5, 7
             strow   is_gf, 7, 6, 8
             strow   is_gf, 8, 7, 9
             strow   is_gf, 9, 8, 0
     
             end     macro
     
     
             strows  0
             strows  1

The source code of the sample program is provided in file samples/maze_asm.c in the package distribution and is also given below. Command make builds the sample program if the QSMM package is configured by the configure script to use the Curses library. See file INSTALL at the root of the package distribution, for information on the configure script.

     #include <assert.h>
     #include <stdlib.h>
     #include <string.h>
     #include <unistd.h>
     
     #if defined(HAVE_CURSES_H)
     #   include <curses.h>
     #elif defined(HAVE_NCURSES_CURSES_H)
     #   include <ncurses/curses.h>
     #endif
     
     #include <qsmm/qsmm.h>
     
     #define NVISIT  400
     #define NSTATE  512
     #define MAX_X   8
     #define MAX_Y   8
     #define ENTRY_X 1
     #define ENTRY_Y 8
     
     
     #define ERREXIT(fmt, ...)                                                 \
         do {                                                                  \
             fprintf(stderr,(fmt), ## __VA_ARGS__);                            \
             fprintf(stderr,"\n");                                             \
             goto Exit;                                                        \
         }                                                                     \
         while (0)
     
     
     #define CHK_FAIL(func, ...)                                               \
         do {                                                                  \
             int rc=func(__VA_ARGS__);                                         \
             if (rc<0) ERREXIT( #func ": %s", qsmm_err_str(rc));               \
         }                                                                     \
         while (0)
     
     
     enum direct_e {
         DIRECT_NORTH=0,
         DIRECT_EAST =1,
         DIRECT_SOUTH=2,
         DIRECT_WEST =3
     };
     
     
     static char is_gold_found;
     static int visit, n_gold_found=0, path_len=0;
     static qsmm_prg_t prg_asm=0;
     static qsmm_msglist_t msglist=0;
     
     
     static unsigned char get_percept(const char **picture_pp, int xx, int yy) {
         unsigned char result=0;
         int col=xx*3+1, row=yy*2+1;
         assert(picture_pp[row][col]==' ' && picture_pp[row][col+1]==' ');
         assert((picture_pp[row-1][col]==' ' &&
                 picture_pp[row-1][col+1]==' ') ||
                (picture_pp[row-1][col]!=' ' &&
                 picture_pp[row-1][col+1]!=' '));
         assert((picture_pp[row+1][col]==' ' &&
                 picture_pp[row+1][col+1]==' ') ||
                (picture_pp[row+1][col]!=' ' &&
                 picture_pp[row+1][col+1]!=' '));
         if (picture_pp[row-1][col]==' ' && picture_pp[row-1][col+1]==' ')
             result|=1 << DIRECT_NORTH;
         if (picture_pp[row][col+2]==' ') result|=1 << DIRECT_EAST;
         if (picture_pp[row+1][col]==' ' && picture_pp[row+1][col+1]==' ')
             result|=1 << DIRECT_SOUTH;
         if (picture_pp[row][col-1]==' ') result|=1 << DIRECT_WEST;
         return result;
     }
     
     
     static int opaque_maze(enum direct_e direct,
                            unsigned char *percept_p) {
         static const char *picture[]={
             // 0  1  2  3  4  5  6  7  8
             "x-----+--x--#..#--x--------+",
             "|     |  |        |        |",  // 0
             "+--+  |  |  #--#  |  +  +  |",
             "|     |  |     |  |  |  |  |",  // 1
             "|  +--+  |  +--+  +--+  *  *",
             "|        |     |  |     |  |",  // 2
             "x--+  +--x-----+  x--+  *--*",
             "|     |  |        |        |",  // 3
             "|  +--+  +  +  +  |  +--+  |",
             "|           |  |  |     |  |",  // 4
             "|  +--+  +--+  |  |  +--+  |",
             "|  |     |     |  |     |  |",  // 5
             "x--+  +--x--+  +--x-----+  |",
             "|     |  |        |        |",  // 6
             "+--+  +  |  +--+  +--+  +--+",
             "|     |  |     |           |",  // 7
             "|  +--+  |  +--+  +--+  +--+",
             "|        |     |  |        |",  // 8
             "+--+..+--x-----+--x--------+"
         };
         static char is_gf=0;
         static int path_len, xx=-1, yy=-1;
         char ss[128];
         unsigned char percept;
         int col, row, result=0;
         if (xx<0 || yy<0) {
             if (visit==NVISIT-1) {
                 if (!initscr()) exit(2);
                 noecho();
                 for (row=0; row<=(MAX_Y+1)*2+1; row++)
                     mvaddstr(row,0,picture[row]);
                 row=(MAX_Y+1)*2+3;
                 mvaddstr(row,0,"Press [Space] to start moving");
                 while (getch()!=' ') ;
                 sprintf(ss,"%64s","");
                 mvaddstr(row,0,ss);
             }
             xx=ENTRY_X;
             yy=ENTRY_Y;
             path_len=0;
         }
         assert(xx>=0 && xx<=MAX_X);
         assert(yy>=0 && yy<=MAX_Y);
         if (get_percept(picture,xx,yy) & (1 << direct)) {
             col=xx*3+1;
             row=yy*2+1;
             switch (direct) {
                 case DIRECT_NORTH: yy--; break;
                 case DIRECT_EAST:  xx++; break;
                 case DIRECT_SOUTH: yy++; break;
                 case DIRECT_WEST:  xx--; break;
                 default: assert(0);
             }
             percept=get_percept(picture,xx,yy);
             path_len++;
             if (visit==NVISIT-1) mvaddstr(row,col,"  ");
             col=xx*3+1;
             row=yy*2+1;
             if (visit==NVISIT-1) {
                 int picture_w=strlen(picture[0]);
                 mvaddstr(row,col,"[]");
                 sprintf(ss," Gold found: %d",is_gf);
                 mvaddstr(1,picture_w+2,ss);
                 sprintf(ss,"Path length: %d",path_len);
                 mvaddstr(3,picture_w+2,ss);
                 move((MAX_Y+1)*2+3,0);
                 refresh();
                 usleep(125000);
             }
             if (picture[row-1][col-1]=='*' && picture[row-1][col+2]=='*' &&
                 picture[row+1][col-1]=='*' && picture[row+1][col+2]=='*') {
                 if (!is_gf) {
                     is_gf=1;
                     result=1;
                 }
             }
             else if (picture[row-1][col-1]=='#' && picture[row-1][col+2]=='#' &&
                      picture[row+1][col-1]=='#' && picture[row+1][col+2]=='#') {
                 is_gf=0;
                 result=2;
                 xx=-1;
                 yy=-1;
                 if (visit==NVISIT-1) {
                     row=(MAX_Y+1)*2+3;
                     mvaddstr(row,0,"Press [Q] to exit");
                     while (1) {
                         int key=getch();
                         if (key=='q' || key=='Q') break;
                     }
                     endwin();
                 }
             }
         }
         else {
             percept=get_percept(picture,xx,yy)+16;
             result=3;
         }
         if (is_gf) percept+=32;
         if (percept_p) *percept_p=percept;
         return result;
     }
     
     
     static QSMM_INSTR_META_CLASS(mv) {
         const char *ccp;
         int rc;
         enum direct_e direct=0;
         if (QSMM_HAS_INSTR_CLASS(qsmm_evt))
             qsmm_get_eh_instr_param(qsmm,sizeof(direct),&direct);
         switch (qsmm_evt) {
             case QSMM_EVT_INSTR_CLASS_INIT:
                 switch (direct) {
                     case DIRECT_NORTH: ccp="north"; break;
                     case DIRECT_EAST:  ccp="east";  break;
                     case DIRECT_SOUTH: ccp="south"; break;
                     case DIRECT_WEST:  ccp="west";  break;
                     default: assert(0);
                 }
                 qsmm_set_eh_instr_param_str_f(qsmm,"%s",ccp);
                 qsmm_set_eh_noutcome(qsmm,64);
                 break;
             case QSMM_EVT_ACTIVATE: {
                 unsigned char percept=0;
                 rc=opaque_maze(direct,&percept);
                 qsmm_time_delta(qsmm,1);
                 switch (rc) {
                     case 0:
                     case 3:
                         break;
                     case 1:
                         is_gold_found=1;
                         n_gold_found++;
                         break;
                     case 2:
                         if (is_gold_found) qsmm_spur_delta(qsmm,1,1);
                         qsmm_return_to_caller_node(qsmm);
                         break;
                     default:
                         assert(0);
                 }
                 if (rc!=2) {
                     if (rc!=3) path_len++;
                     qsmm_set_instr_outcome(qsmm,percept);
                 }
                 break;
             }
         }
         return 0;
     }
     
     
     static QSMM_INSTR_CLASS_SET(walker) {
         switch (qsmm_evt) {
             case QSMM_EVT_ENT_INIT: {
                 int nstate;
                 enum direct_e direct;
                 direct=DIRECT_NORTH, QSMM_REG_INSTR_CLASS_PARAM(mv,direct);
                 direct=DIRECT_EAST,  QSMM_REG_INSTR_CLASS_PARAM(mv,direct);
                 direct=DIRECT_SOUTH, QSMM_REG_INSTR_CLASS_PARAM(mv,direct);
                 direct=DIRECT_WEST,  QSMM_REG_INSTR_CLASS_PARAM(mv,direct);
                 if (prg_asm)
                     nstate=qsmm_get_prg_nstate(qsmm, __FUNCTION__,
                                                QSMM_ASM_TEMPLATE, prg_asm, 0);
                 else nstate=NSTATE;
                 printf("nstate = %d\n",nstate);
                 qsmm_set_nstate_max(qsmm,__FUNCTION__,nstate);
                 QSMM_NODE_CREATE(0);
                 break;
             }
             case QSMM_EVT_ENGINE_INIT:
                 if (prg_asm) qsmm_node_asm(qsmm, 0, QSMM_ASM_TEMPLATE,
                                            prg_asm, msglist);
                 break;
             case QSMM_EVT_NODE_ENTER: {
                 unsigned char percept=0;
                 is_gold_found=0;
                 opaque_maze(DIRECT_NORTH,&percept);
                 break;
             }
         }
         return 0;
     }
     
     
     int main(int argc, char **argv) {
         const char *ccp;
         int seed=0, exit_code=1;
         qsmm_t qsmm=0;
         qsmm_prg_t prg_disasm=0;
         FILE *file_disasm_p=0;
         struct qsmm_desc_s desc;
         struct qsmm_disasm_desc_s disasm_desc;
         if (argc>2) {
             CHK_FAIL(qsmm_msglist_create,&msglist);
             CHK_FAIL(qsmm_parse_asm_source_file, argv[2],
                      QSMM_PARSE_ASM_PREPROCESS, 0, 0, msglist, &prg_asm);
         }
         memset(&desc,0,sizeof(desc));
         desc.dont_use_instr_class_weights=1;
         desc.is_large_env=1;
         desc.is_large_opt=1;
         desc.nspur=2;
         desc.stack_sz_max=1;
         desc.profile_pool_env_sz=2;
         desc.profile_pool_opt_sz=2;
         desc.compat=1;
         desc.sparse_fill_max=0.2;
         CHK_FAIL(qsmm_create,&desc,&qsmm);
         QSMM_REG_INSTR_META_CLASS(qsmm,mv,0);
         QSMM_REG_INSTR_CLASS_SET(qsmm,walker,0);
         qsmm_engine_create(qsmm);
         if (argc>1 && (seed=atoi(argv[1]))<0) {
             qsmm_set_random(qsmm,1);
             seed=-seed;
         }
         qsmm_rng_seed(qsmm_get_rng(qsmm),seed);
         for (visit=0; visit<NVISIT; visit++) {
             qsmm_node_call_default(qsmm,0,0);
             printf("visit %d: ngf=%d, path_len=%d, ngf/path_len=%.8f\n",
                    visit+1, n_gold_found, path_len,
                    (double) n_gold_found/path_len);
         }
         memset(&disasm_desc,0,sizeof(disasm_desc));
         disasm_desc.use_stt_start=1;
         disasm_desc.use_stt_state=1;
         disasm_desc.use_stt_lookup=1;
         disasm_desc.use_stt_abort=1;
         disasm_desc.use_choice=1;
         disasm_desc.use_abort_1=1;
         disasm_desc.fq_goto_min=1;
         disasm_desc.fq_action_min=1;
         if (argc<3) {
             disasm_desc.prob_goto_min=0.005;
             disasm_desc.prob_action_min=0.005;
         }
         qsmm_node_disasm(qsmm,0,&disasm_desc,&prg_disasm);
         if (!(file_disasm_p=fopen(ccp="prg_disasm","w")))
             ERREXIT("%s: failed to open the file for writing",ccp);
         CHK_FAIL(qsmm_prg_dump,prg_disasm,0,file_disasm_p);
         exit_code=0;
     
     Exit:
         qsmm_prg_destroy(prg_disasm);
         qsmm_prg_destroy(prg_asm);
         qsmm_destroy(qsmm);
         if (msglist) {
             qsmm_msglist_dump(msglist,0,"BUFFER",0,stderr);
             qsmm_msglist_destroy(msglist);
         }
         if (file_disasm_p) fclose(file_disasm_p);
         return exit_code;
     }

Here is given sample program output.

     $ ./maze-asm -1
     nstate = 512
     visit 1: ngf=0, path_len=94, ngf/path_len=0.00000000
     visit 2: ngf=0, path_len=800, ngf/path_len=0.00000000
     ...
     visit 201: ngf=37, path_len=179608, ngf/path_len=0.00020600
     visit 202: ngf=38, path_len=180284, ngf/path_len=0.00021078
     ...
     visit 399: ngf=89, path_len=402678, ngf/path_len=0.00022102
     visit 400: ngf=90, path_len=405574, ngf/path_len=0.00022191
     $ ./maze-asm 1
     nstate = 512
     visit 1: ngf=0, path_len=94, ngf/path_len=0.00000000
     visit 2: ngf=0, path_len=800, ngf/path_len=0.00000000
     ...
     visit 200: ngf=175, path_len=98092, ngf/path_len=0.00178404
     visit 201: ngf=176, path_len=98488, ngf/path_len=0.00178702
     ...
     visit 399: ngf=373, path_len=180780, ngf/path_len=0.00206328
     visit 400: ngf=374, path_len=181180, ngf/path_len=0.00206425
     $ ./maze-asm -1 prg_maze
     nstate = 200
     visit 1: ngf=1, path_len=3086, ngf/path_len=0.00032404
     visit 2: ngf=1, path_len=3360, ngf/path_len=0.00029762
     ...
     visit 200: ngf=43, path_len=192884, ngf/path_len=0.00022293
     visit 201: ngf=43, path_len=193488, ngf/path_len=0.00022224
     ...
     visit 399: ngf=84, path_len=381448, ngf/path_len=0.00022021
     visit 400: ngf=84, path_len=382074, ngf/path_len=0.00021985
     $ ./maze-asm 1 prg_maze
     nstate = 200
     visit 1: ngf=1, path_len=3086, ngf/path_len=0.00032404
     visit 2: ngf=2, path_len=3138, ngf/path_len=0.00063735
     ...
     visit 200: ngf=200, path_len=13434, ngf/path_len=0.01488760
     visit 201: ngf=201, path_len=13486, ngf/path_len=0.01490435
     ...
     visit 399: ngf=399, path_len=23782, ngf/path_len=0.01677739
     visit 400: ngf=400, path_len=23834, ngf/path_len=0.01678275

As it can be seen from the above output, the average amount of gold found per one move in the labyrinth in a normal mode of operation of the sample program with random seed 1 and without using the profile assembler program is about 10 times greater than in the completely random mode of operation. An average amount of gold found in a normal mode of operation with random seed 1 and the use of the profile assembler program is about 80 times greater than in the completely random mode of operation.


Next: , Previous: Assembler Programs, Up: Top

6 Miscellaneous Topics

In this chapter secondary topics are covered for which there was no place in other chapters of the manual.


Next: , Up: Miscellaneous Topics

6.1 Random Number Generators

Within the QSMM framework, a random number generator is used by an optimal action generation engine as a source of randomness when stochastically emitting output signals according to their probabilities.

Standard random number generators used within the framework are actually pseudorandom number generators. A developer can also supply custom random number generators for using by the framework. Those custom random number generators may actually be pseudorandom number generators or may provide real random numbers, e.g. obtained using some physical process.


Next: , Up: Random Number Generators

6.1.1 Creating a Random Number Generator

A random number generator is referred to by a handle.

— Data type: qsmm_rng_t

This is a type for a handle of random number generator. It is a pointer, so variables of this type can have zero value. Functions qsmm_rng_create and qsmm_rng_create_custom allocate a new handle. Function qsmm_rng_destroy frees an existing handle.

After allocating a handle, it can be passed to API functions that take argument qsmm_rng_t until the handle is freed. An allocated handle can also be set as the value of field rng of structures that specify parameters of creating an actor or a multinode model. A lifetime of the allocated handle should be longer than a lifetime of the actor or the multinode model.

Function qsmm_get_actor_rng returns a handle of random number generator specified in field rng of structure qsmm_actor_desc_s when creating the actor, or the handle of an instance of default random number generator allocated automatically if 0 is specified in that field. Function qsmm_get_rng returns a handle of random number generator specified in field rng of structure qsmm_desc_s when creating the multinode model, or the handle of an instance of default random number generator allocated automatically if 0 is specified in that field.

To create a random number generator, which is an instance of default random number generator, the following function can be used.

— Function: int qsmm_rng_create (qsmm_rng_t *rng_p)

This function creates a random number generator and stores its newly allocated handle in *rng_p. The function creates either a random number generator, which type is defined by a user-supplied function qsmm_proxy_func_t and its parameter set by a call to qsmm_set_rng_default (see Custom Random Number Generators, for more information), or a random number generator of a standard type (defined when configuring the package) if the user-supplied function is not set.

The function returns a non-negative value on success or a negative error code on failure in creating a random number generator. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Argument rng_p is 0.
QSMM_ERR_NOMEM
There was not enough memory to create a random number generator.

By default, the configure script configures the package to use a pseudorandom number generator implemented by function rand from the standard C library, as the standard random number generator. This mode of operation does not require dependency on an external library but is not recommended. The recommended mode is to use a pseudorandom number generator provided by the GNU Scientific Library, as the standard random number generator. See file INSTALL at the root of the package distribution, for information on how to configure the package to use that library.

One of disadvantages of using a pseudorandom number generator implemented by function rand from the standard C library is that only one instance of the pseudorandom number generator exists, and different handles will actually correspond to a single pseudorandom number generator. This will cause problems when seeding pseudorandom number generators represented by different handles.

A function described below can be used to create a random number generator on the basis of a user-supplied function.

— Function: int qsmm_rng_create_custom (qsmm_proxy_func_t rng_func, void *paramp, qsmm_rng_t *rng_p)

This function creates a random number generator on the basis of function rng_func and its parameter paramp and stores a newly allocated handle of the random number generator in *rng_p. Function rng_func must implement an interface for abstract random number generator. See Custom Random Number Generators, for a description of the interface.

The function returns a non-negative value on success or a negative error code on failure in creating a random number generator. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Argument rng_p is 0.
QSMM_ERR_NOMEM
There was not enough memory to create a random number generator.

To destroy a random number generator, the following function can be used.

— Function: void qsmm_rng_destroy (qsmm_rng_t rng)

This function destroys a random number generator specified by handle rng. After destruction of the random number generator, the handle must not be used. If rng is 0, then the function will do nothing.

An application program is only allowed to destroy random number generators, which it has created explicitly by function qsmm_rng_create or qsmm_rng_create_custom. For example, if the application program destroys a random number generator, which was automatically created for a multinode model and returned by function qsmm_get_rng, then a segmentation fault will occur later.


Next: , Previous: Creating a Random Number Generator, Up: Random Number Generators

6.1.2 Generating Random Numbers

To generate uniformly distributed random numbers, the following functions can be used.

— Function: int qsmm_rng_uniform_int (qsmm_rng_t rng, int nn)

This function returns a new integer random number, uniformly distributed in the range 0 (inclusive) to nn (exclusive), produced using random number generator rng. If nn is less than 1, then negative error code QSMM_ERR_INVAL will be returned.

— Function: double qsmm_rng_uniform (qsmm_rng_t rng)

This function returns a new random number, uniformly distributed in the range 0 (inclusive) to 1 (exclusive), produced using random number generator rng.

— Function: double qsmm_rng_uniform_pos (qsmm_rng_t rng)

This function returns a new (positive) random number, uniformly distributed in the range 0 to 1 (exclusive), produced using random number generator rng.

To seed a pseudorandom number generator, the following function can be used.

— Function: void qsmm_rng_seed (qsmm_rng_t rng, int seed)

This function seeds pseudorandom number generator rng using seed. A seed specifies a stream of pseudorandom numbers produced by the generator. A pseudorandom number generator created on the basis of a user-supplied function might not support seeding.


Previous: Generating Random Numbers, Up: Random Number Generators

6.1.3 Custom Random Number Generators

The type of custom random number generator is defined by a user-supplied function qsmm_proxy_func_t and its parameter. Instances of custom random number generator can be created by function qsmm_rng_create_custom. The type of custom random number generator can be set as the default type of random number generator. Instances of default random number generator are created by function qsmm_rng_create, which is implicitly called by functions qsmm_actor_create and qsmm_create when field rng of structure qsmm_actor_desc_s or qsmm_desc_s has zero value.

To retrieve or set a user-supplied function qsmm_proxy_func_t and its parameter, which define the type of default random number generator, the following functions can be used.

— Function: void qsmm_get_rng_default (qsmm_proxy_func_t *rng_func_p, void **param_pp)

This function retrieves the type of default random number generator, which is defined by a user-supplied function qsmm_proxy_func_t and its parameter. If rng_func_p is not 0, then *rng_func_p will be set to a pointer to the user-supplied function or to 0 if a standard random number generator, which type is defined when configuring the package, is used as a default random number generator. If param_pp is not 0, then *param_pp will be set to a user parameter of function qsmm_proxy_func_t.

— Function: void qsmm_set_rng_default (qsmm_proxy_func_t rng_func, void *paramp)

This function sets the type of default random number generator, which is defined by function rng_func and its parameter paramp. If rng_func is 0, then a standard random number generator, which type is defined when configuring the package, will be used as a default random number generator.

A function, which along with its user parameter defines the type of random number generator, has a prototype of an abstract proxy function that in future versions of the package may be used to perform operations on other objects.

— Data type: qsmm_proxy_func_t

This is a type of a pointer to an abstract proxy function. To this type the following declaration corresponds:

          typedef int (*qsmm_proxy_func_t)(
              int cmd,
              int in_sz,
              int out_sz,
              const void *in_p,
              void *out_p,
              void *paramp);

A command identifier, which specifies an operation the proxy function should perform, is given via argument cmd. Input command parameters are given via buffer in_p of length in_sz bytes. Output command parameters, i.e. results of command invocation, should be written to buffer out_p of length out_sz (or updated in that buffer). A user parameter, specified when setting the proxy function, is passed via argument paramp.

Upon successful completion, the proxy function must return a non-negative value. In the case of error, the proxy function should return a negative value. Specific interpretation of a value returned depends on an object the function represents and on an operation performed by the function.

Possible values of cmd and expected input parameters and invocation results of commands of a proxy function that implements an interface for abstract random number generator are described below.

— Macro: QSMM_RNG_CMD_CREATE

Create a random number generator. A pointer to an object, which represents the random number generator, should be written to buffer out_p. The value of out_sz is always equal to sizeof(void *). For example, to return pointer rng_state_p to an allocated instance of structure, in which the state of a created random number generator is stored, use a line of code like this:

          *((void **) out_p)=rng_state_p;

The proxy function is permitted to return negative error code QSMM_ERR_NOMEM, indicating that there was not enough memory to allocate an object, which represents the random number generator. This error code will be returned by function qsmm_rng_create or qsmm_rng_create_custom called to create the random number generator.

— Macro: QSMM_RNG_CMD_DESTROY

Destroy a random number generator. A pointer to an object, which represents the random number generator, is passed via buffer in_p. The value of in_sz is always equal to sizeof(void *). For example, to obtain pointer rng_state_p to an allocated instance of structure, in which the state of a random number generator is stored, for subsequent destruction, use a line of code like this:

          rng_state_p=*((void **) in_p);

The proxy function is only permitted to return a non-negative value.

— Macro: QSMM_RNG_CMD_GENERATE

Generate a random number. A pointer to an object, which represents a random number generator, is passed via buffer in_p. The value of in_sz is always equal to sizeof(void *). A generated random number uniformly distributed in the range 0 (inclusive) to 1 (exclusive) must be written to buffer out_p. The value of out_sz is always equal to sizeof(double). For example, to return generated random number rnd, use a line of code like this:

          *((double **) out_p)=rnd;

The proxy function is only permitted to return a non-negative value.

— Macro: QSMM_RNG_CMD_SEED

Seed a pseudorandom number generator. An instance of structure qsmm_rng_cmd_seed_in_s (see below), in which there are stored a random seed (the second argument of function qsmm_rng_seed) and a pointer to an object, which represents the pseudorandom number generator, is passed via buffer in_p. The value of in_sz is always equal to sizeof(struct qsmm_rng_cmd_seed_in_s). To obtain seeding parameters, you might use lines of code like these:

          const struct qsmm_rng_cmd_seed_in_s *cmd_in_p=in_p;
          rng_state_p=cmd_in_p->rng_object_p;

When the pseudorandom number generator does not support the seeding operation, the proxy function is permitted to return negative error code QSMM_ERR_NOSYS. However, this error code is currently ignored, because function qsmm_rng_seed returns void.

The following structure is used to pass parameters of seeding operation.

— Structure: qsmm_rng_cmd_seed_in_s

This structure is used to pass input parameters of operation of seeding a pseudorandom number generator. It contains the following fields.

— Field: int seed

A random seed value.

— Field: void * rng_object_p

A pointer to an object that represents the pseudorandom number generator.

Below there is given the example source code of a proxy function that implements the interface for abstract random number generator.

     #include <stdlib.h>
     #include <qsmm/qsmm.h>
     
     static int rng_proxy(int cmd,
                          int in_sz,
                          int out_sz,
                          const void *in_p,
                          void *out_p,
                          void *paramp) {
         unsigned int *seed_p;
         switch (cmd) {
             case QSMM_RNG_CMD_CREATE:
                 if (!(seed_p=malloc(sizeof(*seed_p)))) return QSMM_ERR_NOMEM;
                 *seed_p=1;
                 *((void **) out_p)=seed_p;
                 break;
             case QSMM_RNG_CMD_DESTROY:
                 free(*((void **) in_p));
                 break;
             case QSMM_RNG_CMD_GENERATE:
                 seed_p=*((void **) in_p);
                 *seed_p=*seed_p*1103515245+12345;
                 *((double *) out_p)=(*seed_p/65536)%32768/32768.0;
                 break;
             case QSMM_RNG_CMD_SEED: {
                 const struct qsmm_rng_cmd_seed_in_s *cmd_in_p=in_p;
                 seed_p=cmd_in_p->rng_object_p;
                 *seed_p=cmd_in_p->seed;
                 break;
             }
         }
         return 0;
     }

To set this proxy function as a default random number generator, use line of code

     qsmm_set_rng_default(&rng_proxy,0);


Next: , Previous: Random Number Generators, Up: Miscellaneous Topics

6.2 Ordinary and Sparse Vectors

In QSMM, an ordinary or sparse vector is referred to by a vector handle.

— Data type: qsmm_vec_t

This is a type for a vector handle. It is a pointer, so variables of this type can have zero value. The handle of a vector, which holds probabilities of emitting output signals by an actor, is returned by function qsmm_get_actor_choice_sig_prob_vec. The handle of a newly allocated vector, which is a copy of another vector, is returned by function qsmm_vec_clone. After using the copy, its handle should be freed by function qsmm_vec_destroy.

To get the number of accessible vector elements, the following function can be used.

— Function: int qsmm_get_vec_npos (qsmm_vec_t vec)

This function returns the number of accessible elements of vector vec. The returned value is always non-negative. For an ordinary vector, this number might be equal to the length of a segment of elements, which values were set for the vector. For a sparse vector, this number is equal to the number of elements, which values were set for the vector. Normally, it is the number of non-zero elements in the sparse vector.

To get the value of an element of a vector, the following function can be used.

— Function: int qsmm_get_vec_elm_by_pos (qsmm_vec_t vec, int pos, double *valp)

This function sets *valp to a value of an element of vector vec at position pos. If valp is 0, then *valp will not be set. The value of pos must be non-negative and less than the number of accessible vector elements.

On success, the function returns a non-negative index of an element of the vector that corresponds to position pos. If pos is negative or is greater than or equal to a value returned by function qsmm_get_vec_npos for the vector, then negative error code QSMM_ERR_NOTFOUND will be returned.

To get a position of an element of a vector by its index, the following function can be used.

— Function: int qsmm_get_vec_pos_by_idx (qsmm_vec_t vec, int idx)

This function returns the position of an element of vector vec at index idx. The position is a non-negative number less than the number of accessible vector elements returned by function qsmm_get_vec_npos for the vector.

On success, the function returns a non-negative position of an element of the vector that corresponds to index idx. If there is no such position, then negative error code QSMM_ERR_NOTFOUND will be returned. When idx is non-negative and is less than the number of vector dimensions, this means that the value of the element at index idx is 0.

For example, to get the value of an element of vector vec at index idx, you might use a block of code like this:

     int rc;
     double val=0;  // value of the element
     if ((rc=qsmm_get_vec_pos_by_idx(vec,idx))>=0)
         qsmm_get_vec_elm_by_pos(vec,rc,&val);

To create a copy of a vector, the following function can be used.

— Function: int qsmm_vec_clone (qsmm_vec_t vec_src, qsmm_vec_t *vec_dst_p)

This function creates a copy of vector vec_src and stores its newly allocated vector handle in *vec_dst_p. The copy might occupy a lesser amount of memory than the original because, for an ordinary vector, there might be copied only a segment of elements, which values were set for the vector.

The function returns a non-negative value on success or a negative error code on failure in creating a copy of the vector. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Argument vec_dst_p is 0.
QSMM_ERR_NOMEM
There was not enough memory to create a copy of the vector.

To destroy a copy of a vector created using a call to qsmm_vec_clone, the following function can be used.

— Function: void qsmm_vec_destroy (qsmm_vec_t vec)

This function destroys a vector specified by vector handle vec. After vector destruction, the vector handle must not be used. If vec is 0, then the function will do nothing.

An application program is only allowed to destroy vectors created by function qsmm_vec_clone. If the application program destroys a vector, which handle was returned by function qsmm_get_actor_choice_sig_prob_vec, then a segmentation fault will occur later.


Next: , Previous: Ordinary and Sparse Vectors, Up: Miscellaneous Topics

6.3 Messages and Message Lists

Message lists are used within the QSMM framework to return error, warning, and note messages that can be part of a result of the following operations:

Besides that, an API for working with messages and message lists can be used in applications you develop to return lists of messages from functions.


Next: , Up: Messages and Message Lists

6.3.1 Creating a Message List

A message list is referred to by a message list handle.

— Data type: qsmm_msglist_t

This is a type for a message list handle. It is a pointer, so variables of this type can have zero value. Function qsmm_msglist_create allocates a new message list handle. Function qsmm_msglist_destroy frees an existing message list handle. After allocating a message list handle, it can be passed to API functions that take argument qsmm_msglist_t until the handle is freed.

Use the following function to creates an empty message list, e.g. to pass the list to a function that can fill it with messages.

— Function: int qsmm_msglist_create (qsmm_msglist_t *msglist_p)

This function creates an empty message list and stores its newly allocated handle in *msglist_p.

The function returns a non-negative value on success or a negative error code on failure in creating a message list. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Argument msglist_p is 0.
QSMM_ERR_NOMEM
There was not enough memory to create a message list.

To destroy a message list and all the messages it contains, the following function can be used.

— Function: void qsmm_msglist_destroy (qsmm_msglist_t msglist)

This function destroys a message list specified by message list handle msglist. After message list destruction, the message list handle must not be used. If msglist is 0, then the function will do nothing.


Next: , Previous: Creating a Message List, Up: Messages and Message Lists

6.3.2 Creating Messages

The QSMM API has a special handle type for message objects that can be contained in a message list object. A message object is referred to by a message handle.

— Data type: qsmm_msg_t

This is a type for a message handle. It is a pointer, so variables of this type can have zero value. Functions qsmm_msg_create_f and qsmm_msg_create_fv allocate a new message handle. An existing message handle can be freed explicitly by function qsmm_msg_destroy if the message is not added to a message list, or implicitly when clearing or destroying a message list to which the message was added. After allocating a message handle, it can be passed to API functions that take argument qsmm_msg_t until the handle is freed.

When creating a message object, a category of the message should be specified using the following enumeration.

— Enumeration: qsmm_msg_e

This enumeration specifies the category of a message, which affects how the message will be labeled when dumping a message list that contains the message. The enumeration contains the following elements.

QSMM_MSG_GENERAL
An uncategorized message. No category label will be printed before a message text.
QSMM_MSG_NOTE
A note message. Label ‘note: ’ will be printed before a message text.
QSMM_MSG_WARNING
A warning message. Label ‘warning: ’ will be printed before a message text.
QSMM_MSG_ERROR
An error message. Label ‘error: ’ will be printed before a message text.

To create and destroy a message object, the following functions can be used.

— Function: int qsmm_msg_create_f (qsmm_msg_t *msg_p, enum qsmm_msg_e msg_type, const char *fmt, ...)
— Function: int qsmm_msg_create_fv (qsmm_msg_t *msg_p, enum qsmm_msg_e msg_type, const char *fmt, va_list ap)

These functions create a message of category msg_type and store a newly allocated message handle in *msg_p. Function qsmm_msg_create_f formats the text of the message according to argument fmt and subsequent arguments, the meaning of which is the same as in function printf. Function qsmm_msg_create_fv formats the text of the message according to arguments fmt and ap, the meaning of which is the same as in function vprintf.

The functions return a non-negative value on success or a negative error code on failure in creating a message. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
Argument msg_p is 0.
QSMM_ERR_NOMEM
There was not enough memory to create a message.

— Function: void qsmm_msg_destroy (qsmm_msg_t msg)

This function destroys a message specified by message handle msg. After message destruction, the message handle must not be used. If msg is 0, then the function will do nothing.

The function must not be used to destroy messages that were added to a message list. Those messages are automatically destroyed when clearing the message list by function qsmm_msglist_clear or destroying the message list by function qsmm_msglist_destroy.

There can be a line number associated with a message. If the line number is positive and the name of an input file is specified when dumping the message list, then the name of the file and the line number will be printed before a message text.

— Function: int qsmm_set_msg_lineno (qsmm_msg_t msg, int lineno)

This function associates line number lineno with message msg. If lineno is non-negative and the name of an input file is specified when dumping the message list, then the name of the file will be printed before a message text. If lineno is positive and the name of an input file is specified when dumping the message list, then line number lineno will be printed after the name of the file and before a message text. Thus, if lineno is 0, then only the name of an input file will be printed before a message text, which may indicate that the message pertains to the file in whole. If lineno is -1, then it means that no line number is actually associated with the message, and the name of an input file will not be printed.

The function returns a non-negative value on success or a negative error code on failure. Currently, the following error codes can be returned.

QSMM_ERR_INVAL
The value of lineno is less than -1.
QSMM_ERR_NOMEM
There was not enough memory to perform the operation.


Next: , Previous: Creating Messages, Up: Messages and Message Lists

6.3.3 Adding Messages to a Message List

To add a message to a message list, the following function can be used.

— Function: int qsmm_msglist_add_msg (qsmm_msglist_t msglist, qsmm_msg_t msg)

This function adds message msg to message list msglist. After adding the message to the message list, you must not explicitly destroy the message by function qsmm_msg_destroy. The message will be automatically destroyed by function qsmm_msglist_clear when clearing the message list or by function qsmm_msglist_destroy when destroying the message list.

On success, the function returns a non-negative value. If there is not enough memory to perform the operation, then negative error code QSMM_ERR_NOMEM will be returned.

To append to a message list copies of all messages contained in another message list, the following function can be used.

— Function: int qsmm_msglist_extend (qsmm_msglist_t msglist_dst, qsmm_msglist_t msglist_src)

This function appends to message list msglist_dst copies of all messages contained in message list msglist_src, in the same order.

On success, the function returns a non-negative value. If there is not enough memory to perform the operation, then negative error code QSMM_ERR_NOMEM will be returned.

To clear a message list, the following function can be used.

— Function: void qsmm_msglist_clear (qsmm_msglist_t msglist)

This function removes all messages from message list msglist and destroys them.

To get the number of messages contained in a message list, the following function can be used.

— Function: int qsmm_get_msglist_sz (qsmm_msglist_t msglist)

This function returns the number of messages contained in message list msglist. The returned value is always non-negative.


Previous: Adding Messages to a Message List, Up: Messages and Message Lists

6.3.4 Dumping a Message List

To dump a message list to a stream, the following function can be used.

— Function: void qsmm_msglist_dump (qsmm_msglist_t msglist, const char *prg_name, const char *file_name, unsigned int flags, FILE *filep)

This function dumps all messages contained in message list msglist to stream filep. If prg_name is not 0, then messages that do not have a line number assigned will be prepended with prg_name, which is supposed to be the name of an application program the user has invoked. If prg_name is not 0 and flags has a bit specified by mask QSMM_MSG_DUMP_PRG_NAME_ALL set, then all messages will be prepended with prg_name. If file_name is not 0, then messages that have a non-negative line number assigned will be prepended with file_name, which is supposed to be the name of an input file those messages pertain to.

Thus, the function can dump user-created messages, contained in message lists, in formats listed below. The text of a message might be prepended with a label that depends on a message category defined using enumeration qsmm_msg_e when creating the message.

     message text
     program name: message text
     input file name: message text
     input file name:line number: message text
     program name: input file name: message text
     program name:input file name:line number: message text

When dumping message lists, which messages were created by QSMM API functions, specific messages may be printed in other formats. For example, when dumping a list of messages generated while parsing an assembler program, a message may be prepended with a stack of locations in source assembler files. The stack may indicate places, where ‘include’ preprocessor directives were used to include the contents of a source assembler file that contains an error.


Next: , Previous: Messages and Message Lists, Up: Miscellaneous Topics

6.4 Exchanging Data Packets in a Multithreaded Program

The QSMM framework provides a mechanism for exchanging data packets in multithreaded programs. This mechanism can be helpful e.g. to organize interaction of a system you develop with the environment you model, each executing in a separate thread. When the whole program executes in a single thread, it is sometimes hard to set up communication between different parts of the program.

The mechanism for exchanging data packets will be available if the QSMM package is configured by the configure script to use the POSIX threads API. See file INSTALL at the root of the package distribution, for information about the configure script. Part of the QSMM API, which represents the mechanism, is called Side API, because the mechanism allows multiple sides to take part in the interaction. Datatypes, functions, and macros of the Side API are declared in header file qsmm/side.h, which is installed in a directory for C header files set by the configure script.


Next: , Up: Exchanging Data Packets in a Multithreaded Program

6.4.1 Registering Interaction Sides

Before exchanging data packets, sides, which take part in the interaction, must be registered. A registered side is referred to by a side handle.

— Data type: qsmm_side_t

This is a type for a side handle. It is a pointer, so variables of this type can have zero value. Function qsmm_side_create allocates a new side handle. Function qsmm_side_destroy frees an existing side handle. After allocating a side handle, it can be passed to API functions that take argument qsmm_side_t until the handle is freed.

To register and unregister an interaction side, the following functions can be used.

— Function: int qsmm_side_create (const char *name, qsmm_side_t *side_p)

This function registers interaction side name and stores a newly allocated side handle in *side_p. The value of name is used when dumping the trace of data packets exchange between sides and must not be 0. The function creates a copy of name and stores it in the internal structure.

The function returns a non-negative value on success or a negative error code on failure in registering a side. Currently, the following error codes can be returned.

QSMM_SIDE_ERR_INVAL
Argument side_p is 0.
QSMM_SIDE_ERR_PTHREAD
A POSIX threads API error.
QSMM_SIDE_ERR_NOMEM
There was not enough memory to register a side.

— Function: void qsmm_side_destroy (qsmm_side_t side)

This function unregisters an interaction side specified by handle side. After unregistering the side, its handle must not be used. If side is 0, then the function will do nothing.

To get the name of an interaction side specified by a handle, the following function can be used.

— Function: const char * qsmm_get_side_name (qsmm_side_t side)

This function returns the name of a side specified when creating the side. The returned value is never 0.


Next: , Previous: Registering Interaction Sides, Up: Exchanging Data Packets in a Multithreaded Program

6.4.2 Exchanging Data Packets Between Sides

The following function sends a data packet from one side to another.

— Function: int qsmm_side_send (qsmm_side_t side_from, qsmm_side_t side_to, int data_sz, const void *data_p)

This function sends a data packet of size data_sz bytes pointed by data_p from side side_from to side side_to. The data packet is appended to the end of the queue of received data packets of side side_to. If a thread, in which side side_to is executed, waits for a new data packet using function qsmm_side_recv, then the thread will resume execution, and that function will return the data packet just received.

On success, the function returns a non-negative value. If there is not enough memory to perform the operation, then negative error code QSMM_SIDE_ERR_NOMEM will be returned.

The following function receives a data packet.

— Function: int qsmm_side_recv (qsmm_side_t side, int bufsz, void *bufp)

This function retrieves a data packet from the queue of received data packets of side. The data packet is retrieved to buffer bufp of size bufsz bytes. If bufsz is greater than the size of a data packet retrieved, then remaining contents of buffer bufp will be left intact. If there is no data packet to retrieve, then the function will block until another thread sends a data packet to side.

On success, the function returns a non-negative number equal to the size in bytes of a data packet retrieved. If bufsz is less than the size of a data packet to be retrieved, or if bufsz is positive and bufp is 0, then the function will return negative error code QSMM_SIDE_ERR_INVAL.

To send a data packet from one side to another, it is recommended to use the following macro.

— Macro: QSMM_SIDE_SEND (from, to, msg)

This macro sends data packet msg from side from to side to. The macro is expanded to:

          qsmm_side_send((from), (to), sizeof(msg), &(msg))

Macro argument msg must be a variable, not a constant. For example, to send signal 1 from one side to another, the following lines of code could be used:

     qsmm_sig_t sig_out=1;
     QSMM_SIDE_SEND(side_from,side_to,sig_out);

To receive a data packet, it is recommended to use the following macro.

— Macro: QSMM_SIDE_RECV (side, msgp)

This macro retrieves a data packet from the queue of received data packets of side. The data packet is retrieved to variable *msgp. The macro is expanded to:

          qsmm_side_recv((side), sizeof(*(msgp)), (msgp))

For example, to receive a signal sent to a side, the following lines of code could be used:

     qsmm_sig_t sig_in=QSMM_SIG_INVALID;
     QSMM_SIDE_RECV(side,&sig_in);


Next: , Previous: Exchanging Data Packets Between Sides, Up: Exchanging Data Packets in a Multithreaded Program

6.4.3 Tracing Exchange of Data Packets

The QSMM framework provides facilities for tracing events related to an interaction side. Types of events, which are dumped to the trace log, can be specified using a bitmask defined as a subset of the following macros merged by bitwise “or.”

— Macro: QSMM_SIDE_TRACE_API

Side API calls entry and exit. For every call, the name of an API function, names and values of arguments of the function, and a value returned by the function are dumped.

— Macro: QSMM_SIDE_TRACE_MSG

The contents of data packets sent from the interaction side to other interaction sides.

To get or set a bitmask of types of events, which are dumped to the trace log, the following functions can be used.

— Function: unsigned int qsmm_get_side_trace_flags (qsmm_side_t side)

This function returns a bitmask of types of events related to side, which are dumped to the trace log. It is a bitmask set by the last call to function qsmm_set_side_trace_flags or the default bitmask if that function is not yet called.

— Function: void qsmm_set_side_trace_flags (qsmm_side_t side, unsigned int flags)

This function sets a bitmask of types of events related to side, which are dumped to the trace log, to flags. The correctness of the bitmask is not checked.

Function qsmm_side_create initializes a bitmask of types of events, which are dumped to the trace log, to QSMM_SIDE_TRACE_MSG.

The events will not be written to the trace log unless a stream that represents the trace log is defined for the interaction side. To get or set a stream for the trace log, the following functions can be used.

— Function: FILE * qsmm_get_side_trace_stream (qsmm_side_t side)

This function returns a stream that represents the trace log of side. If the stream is not set, then the function will return 0.

— Function: void qsmm_set_side_trace_stream (qsmm_side_t side, FILE *filep)

This function sets a stream, which represents the trace log of side, to filep. If filep is 0, then event tracing will be disabled for the side.

To write a formatted message to the trace log, e.g. containing additional information on a data packet sent or received, the following functions can be used.

— Function: void qsmm_side_trace_f (qsmm_side_t side, const char *fmt, ...)
— Function: void qsmm_side_trace_fv (qsmm_side_t side, const char *fmt, va_list ap)

These functions write a formatted message to the trace log of side. Character ‘\n’ is appended to the message and the stream buffer is flushed. If the trace log is not set, then the functions will do nothing. The meaning of argument fmt and subsequent arguments of function qsmm_side_trace_f is the same as in function printf. The meaning of arguments fmt and ap of function qsmm_side_trace_fv is the same as in function vprintf.


Next: , Previous: Tracing Exchange of Data Packets, Up: Exchanging Data Packets in a Multithreaded Program

6.4.4 Error Handling

To get a textual description of an error code returned by a Side API function, the following function can be used.

— Function: const char * qsmm_side_err_str (int err)

This function returns a textual description of error code err of the QSMM Side API. For invalid error codes, string "(invalid error code)" is returned.

An interaction side can have an error handler assigned to it. An error handler is a function called in case of an error raised by any Side API function that takes an argument of type qsmm_side_t and can return an error code. The default error handler function of interaction side prints information on an error occurred to stderr and calls exit(2). If there is no error handler function assigned to the interaction side or an error handler function assigned to the interaction side does not terminate program execution and returns, then a Side API function, where the error has occurred, will return corresponding error code.

To get or set an error handler for an interaction side, the following functions can be used.

— Function: void qsmm_get_side_err_handler (qsmm_side_t side, qsmm_side_err_handler_func_t *func_p, void **param_pp)

This function retrieves information on an error handler assigned to interaction side. If func_p is not 0, then *func_p will be set to a pointer to an error handler function assigned to the interaction side or to 0 if there is no error handler function assigned to the interaction side. If param_pp is not 0, then *param_pp will be set to the user parameter of that error handler function.

— Function: void qsmm_set_side_err_handler (qsmm_side_t side, qsmm_side_err_handler_func_t func, void *paramp)

This function assigns an error handler to interaction side. Argument func specifies an error handler function, and argument paramp specifies a user parameter of that function. If func is 0, then no error handler will be used for the interaction side.

The type of a pointer to an error handler function is described below.

— Data type: qsmm_side_err_handler_func_t

This is a type of a pointer to an error handler function. To this type the following declaration corresponds:

          typedef void (*qsmm_side_err_handler_func_t)(
              qsmm_side_t side,
              struct qsmm_side_except_s *except_p,
              void *paramp);

The handle of an interaction side, to which an error pertains to, is passed via argument side. Information on the error occurred is passed via argument except_p. A user parameter specified when setting the error handler function for the interaction side is passed via argument paramp.

A structure, which contains information on an error occurred, is described below.

— Structure: qsmm_side_except_s

This structure is used to convey to the error handler function of interaction side information about an error occurred. The structure contains the following fields.

— Field: const char * func_name

The name of a function within the QSMM library that has raised the error.

— Field: int code

An error code that will be returned by the Side API function after return from the error handler function.


Previous: Error Handling for the Side API, Up: Exchanging Data Packets in a Multithreaded Program

6.4.5 Example Program that Exchanges Data Packets

An example program, which exchanges data packets, is the example program given in Example of Working in Large-scale Mode, rewritten using the POSIX threads API and the QSMM Side API. For the same command-line arguments, the programs produce the same output.

Every visit of the labyrinth is started with creating a thread, where function maze_thread will execute. That thread represents the labyrinth, and the main thread of the program, which represents the system that visits the labyrinth, communicates with the labyrinth thread using the Side API. At exit from the labyrinth, the labyrinth thread exits too.

The source code of the example is provided in file samples/maze_mt.c in the package distribution and is also given below. Command make builds the program if the QSMM package is configured by the configure script to use the POSIX threads API. See file INSTALL at the root of the package distribution, for information on the configure script.

     #include <assert.h>
     #include <stdlib.h>
     #include <string.h>
     
     #include <pthread.h>
     
     #include <qsmm/qsmm.h>
     #include <qsmm/side.h>
     
     #define NVISIT 400
     #define NSTATE 148
     
     
     #define ERREXIT(fmt, ...)                                                 \
         do {                                                                  \
             fprintf(stderr,(fmt), ## __VA_ARGS__);                            \
             fprintf(stderr,"\n");                                             \
             goto Exit;                                                        \
         }                                                                     \
         while (0)
     
     
     enum direct_e {
         DIRECT_NORTH=0,
         DIRECT_EAST =1,
         DIRECT_SOUTH=2,
         DIRECT_WEST =3
     };
     
     
     struct comm_s {
         int         visit;
         qsmm_side_t side_maze;
         qsmm_side_t side_walker;
     };
     
     
     struct maze_response_s {
         char          is_obstacle;
         char          is_gold_found;
         unsigned char percept;
     };
     
     
     static int n_gold_found=0, path_len=0;
     
     
     static void *maze_thread(void *paramp) {
         static int maze[][4]={
             {21,  1, -1, -1},  // 0
             {24,  0, -1, 11},  // 1
             {-1,  9, -1, -1},  // 2
             { 7,  4, -1,  8},  // 3
             {-1, -1, -1,  3},  // 4
             {-1,  6, -1, -1},  // 5
             {-1, 12,  7,  5},  // 6
             {14, -1,  3,  6},  // 7
             {10, -1,  3, 20},  // 8
             {12, 13,  2, -1},  // 9
             { 8, -1, -1, -1},  // 10
             {-1, -1, -1,  1},  // 11
             {-1, 15,  9,  6},  // 12
             {-1, -1,  9, -1},  // 13
             {-1, -1,  7, -1},  // 14
             {25, 16, 12, -1},  // 15
             {-1, -1, -1, 15},  // 16
             {21, -1, -1, -1},  // 17
             {23, -1, -1, -1},  // 18
             {-1, 20, -1, -1},  // 19
             {26,  8, -1, 19},  // 20
             {23, 17,  0, -1},  // 21
             {-1, 24, -1, -1},  // 22
             {28, 18, 21, -1},  // 23
             {-1, 25, 22,  1},  // 24
             {35, -1, 15, 24},  // 25
             {32, 27, 20, -1},  // 26
             {33, 36, -1, 26},  // 27
             {30, 29, 23, -1},  // 28
             {-1, -1, -1, 28},  // 29
             {28, 31, -1, 34},  // 30
             {-1, -1, -1, 30},  // 31
             {26, -1, -1, -1},  // 32
             {-1, -1, 27, -1},  // 33
             {-1, -1, 30, -1},  // 34
             {-1, -1, 25, -1},  // 35
             {-1, -1, -1, 27}   // 36
         };
         char do_dump_path;
         int ii, node_curr=0;
         qsmm_side_t side_maze, side_walker;
         enum direct_e direct;
         struct comm_s *comm_p=paramp;
         struct maze_response_s response;
         memset(&response,0,sizeof(response));
         do_dump_path=(comm_p->visit==NVISIT-1);
         side_maze=comm_p->side_maze;
         side_walker=comm_p->side_walker;
         QSMM_SIDE_RECV(side_maze,&direct);
         while (1) {
             int node_new;
             if (response.percept) {
                 if (do_dump_path) printf(" x");
             }
             else {
                 for (ii=0; ii<4; ii++)
                     if (maze[node_curr][ii]>=0) response.percept|=1 << ii;
                 if (do_dump_path)
                     printf(" %d:%s%s%s%s", node_curr,
                            (response.percept & (1 << DIRECT_NORTH))?"N":"",
                            (response.percept & (1 << DIRECT_EAST ))?"E":"",
                            (response.percept & (1 << DIRECT_SOUTH))?"S":"",
                            (response.percept & (1 << DIRECT_WEST ))?"W":"");
                 if (node_curr==31 && !response.is_gold_found) {
                     if (do_dump_path) printf("(***)");
                     response.is_gold_found=1;
                 }
                 if (node_curr==36) break;
             }
             QSMM_SIDE_SEND(side_maze,side_walker,response);
             QSMM_SIDE_RECV(side_maze,&direct);
             node_new=(node_curr>=0?maze[node_curr][direct]:0);
             if (node_new<0) {
                 response.is_obstacle=1;
                 response.percept=16;
             }
             else {
                 response.is_obstacle=0;
                 response.percept=0;
                 node_curr=node_new;
             }
             assert(node_curr>=0 && node_curr<sizeof(maze)/sizeof(*maze));
         }
         response.percept=-1;
         QSMM_SIDE_SEND(side_maze,side_walker,response);
         if (do_dump_path) fflush(stdout);
         return 0;
     }
     
     
     static QSMM_INSTR_META_CLASS(move) {
         const char *ccp;
         enum direct_e direct=0;
         if (QSMM_HAS_INSTR_CLASS(qsmm_evt))
             qsmm_get_eh_instr_param(qsmm,sizeof(direct),&direct);
         switch (qsmm_evt) {
             case QSMM_EVT_INSTR_CLASS_INIT:
                 switch (direct) {
                     case DIRECT_NORTH: ccp="north"; break;
                     case DIRECT_EAST:  ccp="east";  break;
                     case DIRECT_SOUTH: ccp="south"; break;
                     case DIRECT_WEST:  ccp="west";  break;
                     default: assert(0);
                 }
                 qsmm_set_eh_instr_param_str_f(qsmm,"%s",ccp);
                 qsmm_set_eh_noutcome(qsmm,17);
                 break;
             case QSMM_EVT_ACTIVATE: {
                 qsmm_side_t side_walker;
                 struct comm_s *comm_p=qsmm_get_ptr(qsmm,0);
                 struct maze_response_s maze_response;
                 side_walker=comm_p->side_walker;
                 QSMM_SIDE_SEND(side_walker,comm_p->side_maze,direct);
                 QSMM_SIDE_RECV(side_walker,&maze_response);
                 qsmm_time_delta(qsmm,1);
                 qsmm_set_la_sig(qsmm,0,maze_response.is_gold_found);
                 if (maze_response.percept==(unsigned char) -1) {
                     if (maze_response.is_gold_found) {
                         qsmm_spur_delta(qsmm,1,1);
                         n_gold_found++;
                     }
                     qsmm_return_to_caller_node(qsmm);
                 }
                 else {
                     if (!maze_response.is_obstacle) path_len++;
                     qsmm_set_instr_outcome(qsmm,maze_response.percept);
                 }
                 break;
             }
         }
         return 0;
     }
     
     
     static QSMM_INSTR_CLASS_SET(walker) {
         enum direct_e direct;
         switch (qsmm_evt) {
             case QSMM_EVT_ENT_INIT: {
                 direct=DIRECT_NORTH, QSMM_REG_INSTR_CLASS_PARAM(move,direct);
                 direct=DIRECT_EAST,  QSMM_REG_INSTR_CLASS_PARAM(move,direct);
                 direct=DIRECT_SOUTH, QSMM_REG_INSTR_CLASS_PARAM(move,direct);
                 direct=DIRECT_WEST,  QSMM_REG_INSTR_CLASS_PARAM(move,direct);
                 qsmm_set_nstate_max(qsmm,__FUNCTION__,NSTATE);
                 QSMM_NODE_CREATE(0);
                 break;
             }
             case QSMM_EVT_NODE_ENTER: {
                 qsmm_side_t side_walker;
                 struct comm_s *comm_p=qsmm_get_ptr(qsmm,0);
                 struct maze_response_s maze_response;
                 qsmm_set_la_sig(qsmm,0,0);
                 side_walker=comm_p->side_walker;
                 direct=DIRECT_NORTH;
                 QSMM_SIDE_SEND(side_walker,comm_p->side_maze,direct);
                 QSMM_SIDE_RECV(side_walker,&maze_response);
                 break;
             }
         }
         return 0;
     }
     
     
     int main(int argc, char **argv) {
         int rc, seed=0, exit_code=1;
         qsmm_t qsmm=0;
         pthread_t thread_maze;
         struct comm_s comm;
         struct qsmm_desc_s desc;
         memset(&comm,0,sizeof(comm));
         if ((rc=qsmm_side_create("maze",&comm.side_maze)<0) ||
             (rc=qsmm_side_create("walker",&comm.side_walker)<0))
             ERREXIT("qsmm_side_create: %s",qsmm_side_err_str(rc));
         memset(&desc,0,sizeof(desc));
         desc.dont_use_instr_class_weights=1;
         desc.is_large_env=1;
         desc.is_large_opt=1;
         desc.nspur=2;
         desc.stack_sz_max=1;
         desc.ngram_env_la_sz=1;
         desc.nsig_ngram_env_la=2;
         desc.sparse_fill_max=0.2;
         desc.compat=1;
         if ((rc=qsmm_create(&desc,&qsmm))<0)
             ERREXIT("qsmm_create: %s",qsmm_err_str(rc));
         qsmm_set_ptr(qsmm,0,&comm);
         QSMM_REG_INSTR_META_CLASS(qsmm,move,0);
         QSMM_REG_INSTR_CLASS_SET(qsmm,walker,0);
         qsmm_engine_create(qsmm);
         if (argc>1 && (seed=atoi(argv[1]))<0) {
             qsmm_set_random(qsmm,1);
             seed=-seed;
         }
         qsmm_rng_seed(qsmm_get_rng(qsmm),seed);
         for (comm.visit=0; comm.visit<NVISIT; comm.visit++) {
             if ((rc=pthread_create(&thread_maze,0,&maze_thread,&comm)))
                 ERREXIT("pthread_create() failed with error code %d",rc);
             qsmm_node_call_default(qsmm,0,0);
             pthread_join(thread_maze,0);
         }
         printf("\nn_gold_found=%d\npath_len=%d\nn_gold_found/path_len=%.8f\n",
                n_gold_found, path_len, (double) n_gold_found/path_len);
         exit_code=0;
     
     Exit:
         qsmm_destroy(qsmm);
         qsmm_side_destroy(comm.side_walker);
         qsmm_side_destroy(comm.side_maze);
         return exit_code;
     }


Previous: Exchanging Data Packets in a Multithreaded Program, Up: Miscellaneous Topics

6.5 The Implementation of Functionality of STL map Template

The QSMM framework contains the implementation of functionality of STL map and multimap templates. You can use the implementation in your C programs to create mapping objects without the need to rewrite the programs in C++, which may substantially increase their complexity.

The behavior of the current implementation differs from the behavior of STL map and multimap templates in the following.

Datatypes and functions of the implementation are declared in header file qsmm/map.h, which is installed in a directory for C header files set by the configure script.


Next: , Up: The Implementation of Functionality of STL map Template

6.5.1 Creating Maps and Iterators

A mapping object is referred to by a map handle.

— Data type: qsmm_map_t

This is a type for a map handle, which corresponds to a mapping object that implements the functionality of STL map or multimap template. It is a pointer, so variables of this type can have zero value. A new map handle is allocated by functions qsmm_map_create, qsmm_map_create_sz, qsmm_map_multi_create, and qsmm_map_multi_create_sz. Function qsmm_map_destroy frees an existing map handle. After allocating a map handle, it can be passed to API functions that take argument qsmm_map_t until the handle is freed.

There are the following ways of dealing with keys and values in key-value pairs of a mapping object.

  1. Keys and/or values stored in key-value pair objects are untyped pointers. In this case, the developer should code allocation and deallocation of memory blocks addressed by the untyped pointers explicitly. However, when keys and/or values are integer numbers, they can be converted to the standard intptr_t type and stored as pointers, which does not require manual memory allocation and deallocation.
  2. Contents of keys and/or values are stored at the end of memory blocks of key-value pair objects. The size of key-value pair object is extended by the size of key and/or value memory block. The size of each key and/or value memory block has to be specified when creating the mapping object. Thus, there is no need to perform manual allocation and deallocation of memory blocks for keys and/or values. At the same time, no additional memory blocks are allocated and deallocated by the mapping object itself—memory blocks of keys and/or values are simply parts of memory blocks of key-value pair objects. This method of dealing with keys and/or values speeds up program operation and removes memory overhead caused by storing additional memory management structures.

To create and destroy a mapping object, the following functions can be used.

— Function: qsmm_map_t qsmm_map_create (qsmm_compar_func_t key_compar_func, void *paramp)
— Function: qsmm_map_t qsmm_map_create_sz (int key_sz, int val_sz, qsmm_compar_func_t key_compar_func, void *paramp)
— Function: qsmm_map_t qsmm_map_multi_create (qsmm_compar_func_t key_compar_func, void *paramp)
— Function: qsmm_map_t qsmm_map_multi_create_sz (int key_sz, int val_sz, qsmm_compar_func_t key_compar_func, void *paramp)

These functions create a mapping object. Functions qsmm_map_create and qsmm_map_create_sz create a mapping object that implements the functionality of the STL map template, i.e. a mapping object that may not contain duplicate keys in key-value pairs. Functions qsmm_map_multi_create and qsmm_map_multi_create_sz create a mapping object that implements the functionality of the STL multimap template, i.e. a mapping object that may contain duplicate keys in key-value pairs. However, as distinct from the STL multimap template, a mapping object created by function qsmm_map_multi_create or qsmm_map_multi_create_sz does not provide storing key-value pairs with the same key in order, in which they were added to the mapping object.

Argument key_compar_func specifies a comparison function for keys of the mapping object. Argument paramp specifies a user parameter of that comparison function.

Functions qsmm_map_create and qsmm_map_multi_create create a mapping object, which keys and values are untyped pointers. Functions qsmm_map_create_sz and qsmm_map_multi_create_sz create a mapping object, which keys have size specified by key_sz, and which values have size specified by val_sz. When key_sz is negative, the keys are untyped pointers. When key_sz is positive, each key has size key_sz bytes. The value of key_sz equal to 0 is disallowed. When val_sz is negative, the values are untyped pointers. When val_sz is positive, each value has size val_sz bytes. When val_sz is 0, the mapping object cannot store values, which correspond to keys, i.e. the mapping object is actually a set object, and set elements are the keys of the mapping object.

Call qsmm_map_create(&compar,paramp) is equivalent to call qsmm_map_create_sz(-1,-1,&compar,paramp). Call qsmm_map_multi_create(&compar,paramp) is equivalent to call qsmm_map_multi_create_sz(-1,-1,&compar,paramp).

On success, the functions return a non-zero map handle. If there is not enough memory to create the mapping object, then the functions will return 0.

— Function: void qsmm_map_destroy (qsmm_map_t mm)

This function destroys a mapping object specified by map handle mm. After mapping object destruction, the map handle must not be used. If mm is 0, then the function will do nothing. If keys and/or values in key-value pairs of the mapping object contain untyped pointers to manually created objects, then those objects will not be automatically destroyed by this function and must be destroyed manually before destroying the mapping object. If memory blocks of key-value pairs of the mapping object contain memory blocks of keys and/or values, which require special uninitialization, then that uninitialization must be performed manually before destroying the mapping object.

When creating a mapping object, the developer must provide a function for comparing keys of the mapping object and a user parameter of that function. All key-value pairs in a mapping object are sorted is ascending order of keys. The type of a pointer to the comparison function is described below.

— Data type: qsmm_compar_func_t

This is a type of a pointer to a key comparison function. To this type the following declaration corresponds:

          typedef int (*qsmm_compar_func_t)(
              const void *o1p,
              const void *o2p,
              void *paramp);

A key comparison function should compare two keys pointed by arguments o1p and o2p and return a value greater than 0 if the first key is greater than the second key, a value less than 0 if the first key is less than the second key, or 0 if the keys are equal. A user parameter is passed via argument paramp.

A pointer to a key comparison function and a user parameter of that function specified when creating a mapping object can be obtained using the following functions.

— Function: qsmm_compar_func_t qsmm_map_key_compar_func (qsmm_map_t mm)

This function returns a pointer to a key comparison function specified when creating mapping object mm.

— Function: void * qsmm_map_key_compar_param (qsmm_map_t mm)

This function returns a user parameter of key comparison function specified when creating mapping object mm.

The size of keys and values specified when creating a mapping object can be obtained using the following functions.

— Function: int qsmm_map_key_sz (qsmm_map_t mm)

This function returns the size of a key in each key-value pair of mapping object mm. A positive result indicates the size in bytes of the memory block of key in the memory block of key-value pair object. A negative result indicates that the keys are untyped pointers. The function shall not return 0.

— Function: int qsmm_map_val_sz (qsmm_map_t mm)

This function returns the size of a value in each key-value pair of mapping object mm. A positive result indicates the size in bytes of the memory block of value in the memory block of key-value pair object. A negative result indicates that the values are untyped pointers. Zero result means that the mapping object cannot store values, which correspond to keys, i.e. the mapping object is actually a set object, and set elements are the keys of the mapping object.

To access key-value pairs of mapping objects, map iterators are used. A map iterator is referred to by an iterator handle.

— Data type: qsmm_iter_t

This is a type for an iterator handle. It is a pointer, so variables of this type can have zero value.

A new iterator handle is allocated by functions qsmm_map_iter_create and qsmm_map_multi_iter_create. An iterator handle allocated by function qsmm_map_iter_create should be used with mapping objects, which handles were allocated by functions qsmm_map_create and qsmm_map_create_sz. An iterator handle allocated by function qsmm_map_multi_iter_create should be used with mapping objects, which handles were allocated by functions qsmm_map_multi_create and qsmm_map_multi_create_sz.

Function qsmm_map_iter_destroy frees an existing iterator handle. After allocating an iterator handle, it can be passed to API functions that take argument qsmm_iter_t until the handle is freed.

To create and destroy a map iterator, the following functions can be used.

— Function: qsmm_iter_t qsmm_map_iter_create ()

This function creates an iterator to be used with mapping objects that implement the functionality of the STL map template. On success, the function returns a non-zero iterator handle. If there is not enough memory to create the iterator, then the function will return 0.

— Function: qsmm_iter_t qsmm_map_multi_iter_create ()

This function creates an iterator to be used with mapping objects that implement the functionality of the STL multimap template. On success, the function returns a non-zero iterator handle. If there is not enough memory to create the iterator, then the function will return 0.

— Function: void qsmm_map_iter_destroy (qsmm_iter_t iter)

This function destroys a map iterator specified by iterator handle iter. After map iterator destruction, the iterator handle must not be used. If iter is 0, then the function will do nothing.


Next: , Previous: Creating Maps and Iterators, Up: The Implementation of Functionality of STL map Template

6.5.2 Operations on Maps

In this section, basic operations on mapping objects are described. Every operation corresponds to a method of STL map and multimap templates.

— Function: size_t qsmm_map_size (qsmm_map_t mm)

This function returns the number of key-value pairs contained in mapping object mm. The function corresponds to method size of STL map and multimap templates.

— Function: int qsmm_map_is_empty (qsmm_map_t mm)

This function returns a positive value if mapping object mm does not contain key-value pairs, or zero if it does. Negative values are never returned. The function corresponds to method empty of STL map and multimap templates.

— Function: int qsmm_map_assign (qsmm_map_t dst, qsmm_map_t src)

This function copies all key-value pairs of mapping object src to mapping object dst. All key-value pairs, which were in mapping object dst before the copying, are discarded. Mapping objects src and dst must be both created using either functions qsmm_map_create and qsmm_map_create_sz or functions qsmm_map_multi_create and qsmm_map_multi_create_sz, have the same pointer to the key comparison function and its user parameter.

If keys and/or values stored in key-value pair objects are untyped pointers, then only those pointers will be copied or discarded, memory blocks the pointers may point to will not be affected. If memory blocks of key-value pair objects contain memory blocks of keys and/or values, then the contents of those memory blocks will be copied or discarded.

Function qsmm_map_assign corresponds to operator= of STL map and multimap templates. The function returns a non-negative value on success or a negative value on out of memory error.

— Function: int qsmm_map_insert (qsmm_map_t mm, void *keyp, void *valp, qsmm_iter_t result_loc)

This function inserts a key-value pair in mapping object mm. Argument keyp specifies a key of the pair. Argument valp specifies a value of the pair.

If keys and/or values stored in key-value pair objects are untyped pointers, then the function will insert untyped pointer keyp and/or valp in the mapping object. If memory blocks of key-value pair objects contain memory blocks of keys and/or values, then a memory block pointed by keyp and/or valp will be copied to the key-value pair object being inserted. If memory blocks of key-value pair objects contain memory blocks of values and valp is 0, then the memory block of value in the key-value pair object being inserted will be initialized with zero bytes.

If mapping object mm is created using function qsmm_map_create or qsmm_map_create_sz, then it cannot contain duplicate keys. Uniqueness of keys is determined using a comparison function specified when creating the mapping object. When adding a new key-value pair with a key, which is already contained in the mapping object, the new key-value pair replaces an old key-value pair. Replacing the old pair may cause a memory leak if the pair contains pointers to manually allocated memory blocks. If mapping object mm is created using function qsmm_map_multi_create or qsmm_map_multi_create_sz, then it can contain multiple key-value pairs with the same key.

If result_loc is not 0, then after successful function completion, iterator result_loc will point to a new key-value pair inserted in mapping object mm. The type of iterator result_loc must agree with the type of mapping object mm. Calling the function with non-zero result_loc is useful when memory blocks of key-value pair objects contain memory blocks of values and valp is 0. In this case, a pointer to the memory block of value preinitialized with zero bytes can be obtained by call qsmm_map_iter_val(result_loc) for setting the value.

This function corresponds to method insert of STL map and multimap templates. The function returns a non-negative value on success or a negative value on out of memory error.

— Function: void qsmm_map_clear (qsmm_map_t mm)

This function removes all key-value pairs from mapping object mm. Memory blocks, pointers to which might be held in key-value pairs, are not affected. The function corresponds to method clear of STL map and multimap templates.

— Function: void qsmm_map_find (qsmm_map_t mm, const void *keyp, qsmm_iter_t result)

This function finds in mapping object mm a key-value pair that has a key equal to keyp. If the mapping object is created using function qsmm_map_multi_create or qsmm_map_multi_create_sz, then the function will find in the mapping object the first key-value pair that has a key equal to keyp. Equality of keys is tested using a comparison function specified when creating the mapping object. Function qsmm_map_find corresponds to method find of STL map and multimap templates.

After function completion, iterator result refers to a key-value pair found. If a key-value pair not found, then iterator result will refer to a place just after the last key-value pair in the mapping object. The type of iterator result must agree with the type of mapping object mm.

— Function: void qsmm_map_lower_bound (qsmm_map_t mm, const void *keyp, qsmm_iter_t result)

This function finds in mapping object mm the first key-value pair that has a key greater than or equal to keyp. The function corresponds to method lower_bound of STL map and multimap templates.

After function completion, iterator result refers to a key-value pair found. If a key-value pair not found, then iterator result will refer to a place just after the last key-value pair in the mapping object. The type of iterator result must agree with the type of mapping object mm.

— Function: void qsmm_map_upper_bound (qsmm_map_t mm, const void *keyp, qsmm_iter_t result)

This function finds in mapping object mm the first key-value pair that has a key greater than keyp. The function corresponds to method upper_bound of STL map and multimap templates.

After function completion, iterator result refers to a key-value pair found. If a key-value pair not found, then iterator result will refer to a place just after the last key-value pair in the mapping object. The type of iterator result must agree with the type of mapping object mm.

— Function: void qsmm_map_erase (qsmm_map_t mm, qsmm_iter_t where)

This function removes from mapping object mm a key-value pair addressed by iterator where. Memory blocks, pointers to which might be contained in the key-value pair, are not affected. The function corresponds to method erase of STL map and multimap templates.

Note: when removing a key-value pair from a mapping object, the key of the pair must be uninitialized and/or destroyed after calling function qsmm_map_erase, not before. It is because in the current implementation function qsmm_map_erase may call a key comparison function for the key of a key-value pair being removed, and that key must be a valid object.


Next: , Previous: Operations on Maps, Up: The Implementation of Functionality of STL map Template

6.5.3 Operations on Iterators

In this section operations on map iterators are described.

— Function: int qsmm_map_iter_is_end (qsmm_map_t mm, qsmm_iter_t iter)

This function returns a positive value if iterator iter is a forward (normal) iterator that refers to a place just after the last key-value pair in mapping object mm or if iterator iter is a reverse iterator that refers to a place just before the first key-value pair in mapping object mm, or returns 0 otherwise. The function never returns negative values.

— Function: int qsmm_map_iter_are_equal (qsmm_iter_t iter1, qsmm_iter_t iter2)

This function returns a positive value if iterators iter1 and iter2 refer to the same location in a mapping object, or returns 0 otherwise. The function never returns negative values.

— Function: void qsmm_map_iter_begin (qsmm_map_t mm, qsmm_iter_t result)

This function retrieves a location of the first key-value pair in mapping object mm. After function completion, iterator result refers to that key-value pair. If the mapping object does not contain key-value pairs, then iterator result will refer to a place, commonly designated as a place just after the last key-value pair in the mapping object. The type of iterator result must agree with the type of mapping object mm. The function corresponds to method begin of STL map and multimap templates.

— Function: void qsmm_map_iter_end (qsmm_map_t mm, qsmm_iter_t result)

This function makes iterator result to refer to a place just after the last key-value pair in mapping object mm. The type of iterator result must agree with the type of mapping object mm. The function corresponds to method end of STL map and multimap templates.

— Function: void qsmm_map_iter_rbegin (qsmm_map_t mm, qsmm_iter_t result)

This function makes iterator result to be a reverse iterator that refers to the last key-value pair (in ascending order of keys) in mapping object mm. If the mapping object does not contain key-value pairs, then iterator result will refer to a place, commonly designated as a place just before the first key-value pair in the mapping object. The type of iterator result must agree with the type of mapping object mm. The function corresponds to method rbegin of STL map and multimap templates.

— Function: void qsmm_map_iter_rend (qsmm_map_t mm, qsmm_iter_t result)

This function makes iterator result to be a reverse iterator that refers to a place just before the first key-value pair in mapping object mm. The type of iterator result must agree with the type of mapping object mm. The function corresponds to method rend of STL map and multimap templates.

— Function: void qsmm_map_iter_next (qsmm_iter_t iter)

This function makes iterator iter to refer to the next key-value pair in a mapping object. If iterator iter is a forward (normal) iterator, then it will refer to the next key-value pair in ascending order of keys. If iterator iter is a reverse iterator, then it will refer to the next key-value pair in descending order of keys (i.e. to the previous key-value pair in ascending order of keys).

— Function: void qsmm_map_iter_prev (qsmm_iter_t iter)

This function makes iterator iter to refer to the previous key-value pair in a mapping object. If iterator iter is a forward (normal) iterator, then it will refer to the previous key-value pair in ascending order of keys (i.e. to the next key-value pair in descending order of keys). If iterator iter is a reverse iterator, then it will refer to the previous key-value pair in descending order of keys (i.e. to the next key-value pair in ascending order of keys).

— Function: void qsmm_map_iter_assign (qsmm_iter_t dst, qsmm_iter_t src)

This function makes iterator dst to refer to the same place as iterator src. An indication whether an iterator is forward (normal) or reverse one is also copied from src to dst. Iterators src and dst must be both created using either function qsmm_map_iter_create or function qsmm_map_multi_iter_create.

— Function: void qsmm_map_iter_set_val (qsmm_iter_t iter, void *valp)

This function sets to valp the value part of a key-value pair addressed by iterator iter. An old value is discarded. If a value stored in the key-value pair object is an untyped pointer, then this function will set the value equal to valp. If the memory block of the key-value pair object contains the memory block of value, then this function will replace the contents of the latter memory block with the contents of a memory block pointed by valp.

— Function: void * qsmm_map_iter_key (qsmm_iter_t iter)

This function returns the key component of a key-value pair addressed by iterator iter. If a key stored in the key-value pair object is an untyped pointer, then that pointer will be returned. If the memory block of the key-value pair object contains the memory block of key, then a pointer to the latter memory block will be returned.

— Function: void * qsmm_map_iter_val (qsmm_iter_t iter)

This function returns the value component of a key-value pair addressed by iterator iter. If a value stored in the key-value pair object is an untyped pointer, then that pointer will be returned. If the memory block of the key-value pair object contains the memory block of value, then a pointer to the latter memory block will be returned.


Previous: Operations on Iterators, Up: The Implementation of Functionality of STL map Template

6.5.4 Example of Using a Mapping Object

In the example of using a mapping object, the program counts frequencies of words in a text and prints the table of word frequencies in descending order by frequency. Words, which frequencies are counted by the program, are sequences of alphanumeric characters. All other characters are considered as word delimiters.

The program should be invoked with an argument that specifies the name of a file with an input text. An optional second argument should be a positive integer number that specifies how many most frequent words to print in the resulting frequency table.

The source code of the example is provided in file samples/fqtab.c in the package distribution and is also given below.

     #include <ctype.h>
     #include <stdio.h>
     #include <stdlib.h>
     #include <string.h>
     
     #include <qsmm/map.h>
     
     
     #define ERREXIT(fmt, ...)                       \
         do {                                        \
             fprintf(stderr,(fmt), ## __VA_ARGS__);  \
             fprintf(stderr,"\n");                   \
             goto Exit;                              \
         }                                           \
         while (0)
     
     
     struct word_fq_s {
         char *word;
         int  fq;
     };
     
     
     static qsmm_iter_t iter_word_to_fq=0;
     static qsmm_map_t map_word_to_fq=0;
     
     
     static int word_add(const char *word) {
         qsmm_map_find(map_word_to_fq,word,iter_word_to_fq);
         if (qsmm_map_iter_is_end(map_word_to_fq,iter_word_to_fq)) {
             char *keyp=strdup(word);
             if (!keyp) return -1;
             if (qsmm_map_insert(map_word_to_fq,keyp,0,iter_word_to_fq)<0) {
                 free(keyp);
                 return -1;
             }
         }
         (*((int *) qsmm_map_iter_val(iter_word_to_fq)))++;
         return 0;
     }
     
     
     static int word_compar(const void *o1p, const void *o2p, void *dummyp) {
         return strcmp(o1p,o2p);
     }
     
     
     static int word_fq_compar(const void *o1p, const void *o2p) {
         int result;
         const struct word_fq_s *word_fq_1p=o1p, *word_fq_2p=o2p;
         if ((result=word_fq_2p->fq-word_fq_1p->fq)) return result;
         return strcmp(word_fq_1p->word,word_fq_2p->word);
     }
     
     
     int main(int argc, char **argv) {
         const char *fln_text;
         char *word_p=0;
         int ii, nword, word_sz=0, word_allo=2, n_word_top=0, exit_code=1;
         FILE *file_text_p=0;
         struct word_fq_s *word_fq_p=0;
         if (argc<2) ERREXIT("input file not specified");
         fln_text=argv[1];
         if (!(file_text_p=fopen(fln_text,"r")))
             ERREXIT("%s: failed to open the file",fln_text);
         if (argc>2 && (n_word_top=atoi(argv[2]))<1)
             ERREXIT("invalid number of the most frequent words");
         if (!(word_p=calloc(word_allo,sizeof(*word_p))) ||
             !(iter_word_to_fq=qsmm_map_iter_create()) ||
             !(map_word_to_fq=
               qsmm_map_create_sz(-1,sizeof(int),&word_compar,0)))
             ERREXIT("out of memory");
         while (1) {
             int cc=fgetc(file_text_p);
             if (cc==EOF) {
                 if (ferror(file_text_p))
                     ERREXIT("%s: failed to read the file",fln_text);
                 if (word_sz>0 && word_add(word_p)<0) ERREXIT("out of memory");
                 break;
             }
             if (isalnum(cc)) {
                 if (word_sz+2>word_allo) {
                     char *new_p;
                     int allo=word_allo*3/2;
                     if (!(new_p=realloc(word_p,allo)))
                         ERREXIT("out of memory");
                     word_p=new_p;
                     word_allo=allo;
                 }
                 word_p[word_sz++]=cc;
                 word_p[word_sz]=0;
             }
             else if (word_sz>0) {
                 if (word_add(word_p)<0) ERREXIT("out of memory");
                 *word_p=0;
                 word_sz=0;
             }
         }
         nword=qsmm_map_size(map_word_to_fq);
         if (!(word_fq_p=calloc(nword,sizeof(*word_fq_p))))
             ERREXIT("out of memory");
         for (ii=0, qsmm_map_iter_begin(map_word_to_fq,iter_word_to_fq);
              !qsmm_map_iter_is_end(map_word_to_fq,iter_word_to_fq);
              ii++, qsmm_map_iter_next(iter_word_to_fq)) {
             word_fq_p[ii].word=qsmm_map_iter_key(iter_word_to_fq);
             word_fq_p[ii].fq=*((int *) qsmm_map_iter_val(iter_word_to_fq));
         }
         qsort(word_fq_p,nword,sizeof(*word_fq_p),&word_fq_compar);
         for (ii=0; ii<nword; ii++) {
             printf("%d\t%s\n", word_fq_p[ii].fq, word_fq_p[ii].word);
             if (n_word_top>0 && ii>=n_word_top) break;
         }
         exit_code=0;
     
     Exit:
         if (map_word_to_fq) {
             if (iter_word_to_fq)
                 for (qsmm_map_iter_begin(map_word_to_fq,iter_word_to_fq);
                      !qsmm_map_iter_is_end(map_word_to_fq,iter_word_to_fq);
                      qsmm_map_iter_next(iter_word_to_fq))
                     free(qsmm_map_iter_key(iter_word_to_fq));
             qsmm_map_destroy(map_word_to_fq);
         }
         if (iter_word_to_fq) qsmm_map_iter_destroy(iter_word_to_fq);
         if (file_text_p) fclose(file_text_p);
         if (word_fq_p) free(word_fq_p);
         if (word_p) free(word_p);
         return exit_code;
     }

Below there is given sample program output. The program prints 10 most frequent words contained in its source text.

     $ ./fqtab fqtab.c 10
     86      word
     52      fq
     32      p
     32      to
     32      iter
     32      map
     23      0
     23      if
     21      qsmm
     12      int
     11      ii

Note that frequencies of words calculated for the text of the example program presented in this subsection will differ from frequencies of words calculated for file samples/fqtab.c in the package distribution because the file is prepended with a license block.


Next: , Previous: Miscellaneous Topics, Up: Top

7 Example Programs

In this chapter example programs are described, which demonstrate various aspects of package use and which source code is not included in this manual. Source code for these programs is available in directory samples in the package distribution. The programs do not get installed by command make install.


Next: , Up: Example Programs

7.1 optact

This program demonstrates a behavior of an actor when it emits more frequently output signals immediately followed by greater mean spur increments. However, the program does not reveal a generic aspect of actor's behavior when the frequency of emitting an output signal also depends on spur increments that were made at later time after emitting the output signal.

In the example program, the actor randomly receives one of signal vectors <3, 0, 4>, <1, 2, 5>, <5, 3, 3>, <2, 4, 0>, and <4, 2, 1>, which represent action choice states shown in Figure 2.1 (and one additional action choice state), and emits one of four output signals. The total number of signal vectors received and output signals emitted is defined by macro NSTEP (that has value 40000).

For each action choice state vector, fixed probabilities are assigned to four possible output signals. According to those probabilities, the actor receives spur increment 1 when it emits corresponding output signals. The expected actor behavior is to emit more frequently output signals, to which greater probabilities correspond.

The researcher may want to change the problem statement and to make an actor emit output signals with frequencies proportional to the fixed probabilities. In this case, instead of making spur increment 1 according to a fixed probability, spur increment equal to the logarithm of the fixed probability should be made. There are also other specifics that should be considered when solving this another kind of problem. See a program described in optact-p, where the problem is solved. For example, run that program using command line

     $ ./optact-p -i1 -a -n40000 --dump-prob=learnt 5 4

At the end of its run, the optact program prints a table with the numbers of output signals emitted in each action choice state, i.e. with frequencies of those output signals. The frequencies are printed near fixed probabilities of output signals specified in the program source text. The program also dumps the contents of statistics storage of the actor.

A random seed can be specified by a program argument. If the random seed is non-negative, then the actor will operate normally. If the random seed is negative, then the actor will generate output signals completely randomly. You could compare the resulting frequencies of output signals for these two modes of program execution.

Below there is given sample program output for the completely random mode of operation of the program.

     $ ./optact -1
     < 3 0 4 > : 0.2/1975 0.4/2004 0.3/2010 0.1/1917
     < 1 2 5 > : 0.4/2062 0.1/2071 0.3/1902 0.2/1966
     < 5 3 3 > : 0.2/2010 0.2/2031 0.2/2026 0.4/2000
     < 2 4 0 > : 0.1/1993 0.3/2000 0.5/2032 0.1/2017
     < 4 2 1 > : 0.3/2052 0.3/1924 0.2/2000 0.2/2008
     
     State < 1 2 5 >: sig_cycle_next=7, tmd0=159975, tmc0=119982, s0=10097
       sig_next=6: fq=2062, pd_s=40584, pc_s=30438, sd_s=2903, ave=0.0953742
       sig_next=7: fq=2070, pd_s=40996, pc_s=30747, sd_s=2283, ave=0.0742511
       sig_next=8: fq=1902, pd_s=39252, pc_s=29439, sd_s=2646, ave=0.0898808
       sig_next=9: fq=1966, pd_s=39132, pc_s=29349, sd_s=2264, ave=0.0771406
     
     State < 2 4 0 >: sig_cycle_next=7, tmd0=159967, tmc0=119976, s0=10097
       sig_next=6: fq=1993, pd_s=40632, pc_s=30474, sd_s=2262, ave=0.0742272
       sig_next=7: fq=1999, pd_s=39536, pc_s=29652, sd_s=2664, ave=0.0898422
       sig_next=8: fq=2032, pd_s=39988, pc_s=29991, sd_s=2968, ave=0.098963
       sig_next=9: fq=2017, pd_s=39788, pc_s=29841, sd_s=2199, ave=0.0736906
     
     State < 3 0 4 >: sig_cycle_next=7, tmd0=159983, tmc0=119988, s0=10098
       sig_next=6: fq=1975, pd_s=39632, pc_s=29724, sd_s=2374, ave=0.0798681
       sig_next=7: fq=2003, pd_s=41184, pc_s=30888, sd_s=2894, ave=0.0936933
       sig_next=8: fq=2010, pd_s=39808, pc_s=29856, sd_s=2638, ave=0.0883574
       sig_next=9: fq=1917, pd_s=39340, pc_s=29505, sd_s=2189, ave=0.0741908
     
     State < 4 2 1 >: sig_cycle_next=6, tmd0=159999, tmc0=120000, s0=10098
       sig_next=6: fq=2051, pd_s=41372, pc_s=31029, sd_s=2719, ave=0.0876277
       sig_next=7: fq=1924, pd_s=38692, pc_s=29019, sd_s=2526, ave=0.0870464
       sig_next=8: fq=2000, pd_s=41208, pc_s=30906, sd_s=2460, ave=0.0795962
       sig_next=9: fq=2008, pd_s=38720, pc_s=29040, sd_s=2393, ave=0.0824036
     
     State < 5 3 3 >: sig_cycle_next=9, tmd0=159979, tmc0=119985, s0=10097
       sig_next=6: fq=2010, pd_s=39676, pc_s=29757, sd_s=2380, ave=0.0799812
       sig_next=7: fq=2031, pd_s=40344, pc_s=30258, sd_s=2421, ave=0.0800119
       sig_next=8: fq=2026, pd_s=40760, pc_s=30570, sd_s=2477, ave=0.0810272
       sig_next=9: fq=1999, pd_s=39196, pc_s=29397, sd_s=2819, ave=0.0958941

Below there is given sample program output for the normal mode of operation of the program.

     $ ./optact 1
     < 3 0 4 > : 0.2/1551 0.4/3253 0.3/2295 0.1/807
     < 1 2 5 > : 0.4/3297 0.1/1333 0.3/2136 0.2/1235
     < 5 3 3 > : 0.2/1797 0.2/1674 0.2/1321 0.4/3275
     < 2 4 0 > : 0.1/1223 0.3/2031 0.5/3781 0.1/1007
     < 4 2 1 > : 0.3/2506 0.3/2095 0.2/1624 0.2/1759
     
     State < 1 2 5 >: sig_cycle_next=6, tmd0=159975, tmc0=119982, s0=11849
       sig_next=6: fq=3296, pd_s=64696, pc_s=48522, sd_s=5176, ave=0.106673
       sig_next=7: fq=1333, pd_s=26876, pc_s=20157, sd_s=1741, ave=0.086372
       sig_next=8: fq=2136, pd_s=44124, pc_s=33093, sd_s=3327, ave=0.100535
       sig_next=9: fq=1235, pd_s=24268, pc_s=18201, sd_s=1604, ave=0.088127
     
     State < 2 4 0 >: sig_cycle_next=8, tmd0=159967, tmc0=119976, s0=11848
       sig_next=6: fq=1223, pd_s=26192, pc_s=19644, sd_s=1641, ave=0.083537
       sig_next=7: fq=2031, pd_s=39532, pc_s=29649, sd_s=2855, ave=0.0962933
       sig_next=8: fq=3780, pd_s=74580, pc_s=55935, sd_s=6164, ave=0.110199
       sig_next=9: fq=1007, pd_s=19640, pc_s=14730, sd_s=1185, ave=0.0804481
     
     State < 3 0 4 >: sig_cycle_next=7, tmd0=159983, tmc0=119988, s0=11850
       sig_next=6: fq=1551, pd_s=30904, pc_s=23178, sd_s=2102, ave=0.0906894
       sig_next=7: fq=3252, pd_s=67292, pc_s=50469, sd_s=5375, ave=0.106501
       sig_next=8: fq=2295, pd_s=46132, pc_s=34599, sd_s=3396, ave=0.0981531
       sig_next=9: fq= 807, pd_s=15636, pc_s=11727, sd_s= 975, ave=0.0831415
     
     State < 4 2 1 >: sig_cycle_next=6, tmd0=159999, tmc0=120000, s0=11850
       sig_next=6: fq=2505, pd_s=50620, pc_s=37965, sd_s=3852, ave=0.101462
       sig_next=7: fq=2095, pd_s=41828, pc_s=31371, sd_s=3172, ave=0.101112
       sig_next=8: fq=1624, pd_s=33220, pc_s=24915, sd_s=2354, ave=0.0944812
       sig_next=9: fq=1759, pd_s=34324, pc_s=25743, sd_s=2472, ave=0.0960261
     
     State < 5 3 3 >: sig_cycle_next=9, tmd0=159979, tmc0=119985, s0=11849
       sig_next=6: fq=1797, pd_s=34976, pc_s=26232, sd_s=2448, ave=0.0933211
       sig_next=7: fq=1674, pd_s=34064, pc_s=25548, sd_s=2386, ave=0.0933928
       sig_next=8: fq=1321, pd_s=25768, pc_s=19326, sd_s=1796, ave=0.0929318
       sig_next=9: fq=3274, pd_s=65168, pc_s=48876, sd_s=5219, ave=0.10678


Next: , Previous: optact, Up: Example Programs

7.2 apsamp

This program uses a pair of actors that investigates a state model represented in apsamp_fig. The first actor of the pair guesses current state of the state model. The second actor of the pair emits signals to the state model on the basis of current states guessed by the first actor.

The state model contains 10 states, shown in the figure by circles with state indices inside them. The first actor of the pair works with two spur types, where spur type 0 corresponds to the automatic spur, and spur type 1 corresponds to user spur. The second actor of the pair works only with user spur. The goal of the operation of the pair of actors is to maximize the velocity of increment of user spur.

Arrows represent allowed state transitions. The thick arrow indicates a transition, during which the pair of actors receives user spur increment +1. A number before a slash shown near every arrow indicates a signal that can be emitted to the state model, and which makes the model perform the state transition. A number after a slash shown near every arrow indicates a signal the state model emits to the pair of actors when performing the state transition. If the state model receives from the pair of actors a signal, for which no state transition exists, then current state of the model will be left unchanged, and signal 0 will be emitted to the pair of actors.

As it could be seen from the figure, the optimal behavior of the pair of actors would be continuous emitting sequence of output signals <7, 4, 6, 7>, which gives user spur increment +1.

Sample state model investigated by the pair of actors

Figure 7.1: sample state model investigated by the pair of actors

Unfortunately, the sample state model is too simple and permits guessing the optimal sequence of output signals to emit even without analyzing input signals received from the state model. However, you may experiment with other state models by modifying matrices trans_mat, emiss_mat, and spur_mat hard-coded in the program source text.

Macro NSTEP (that has value 40000) defines the number of steps of interaction of the pair of actors with the state model equal to the number of signals emitted to the state model and to the number of signals received from the state model. At the end of its run, the example program prints the amount of user spur accumulated by the pair of actors.

A random seed can be specified by a program argument. If the random seed is non-negative, then the pair of actors will operate normally. If the random seed is negative, then the pair of actors will generate output signals completely randomly. You could compare the accumulated values of user spur for these two modes of program execution.

The example program can be built for various schemes of using a pair of actors. To build the program for a selected scheme, delete executable and object files of the program if they exist, and run command

     make name_of_executable_of_program_apsamp AP_MODE=name_of_scheme

in directory samples. The supported names of schemes are: ‘S1_S1’, ‘S1_S3’, ‘S1_L3’, ‘S3_S1’, ‘S3_S3’, ‘S3_L3’, ‘L3_L3’. For example, the command might look like this: make apsamp AP_MODE=L3_L3. If a scheme is not specified when building the program, then default scheme ‘S1_S1’ will be used.

The schemes are encoded in the following way. The first letter and digit denote parameters of the first actor of the pair. The second letter and digit denote parameters of the second actor of the pair. Letter ‘S’ denotes a small actor, and letter ‘L’ denotes a large actor. Digit 1 denotes the type of relative probability function similar to QSMM_RELPROB_BUILTIN1 used by an actor. Digit 3 denotes type of relative probability function QSMM_RELPROB_BUILTIN3 used by an actor. The name of the scheme also defines other parameters of the actors.

Here is given the output of the example program built for scheme ‘S1_S1’.

     $ ./apsamp -1
     spur = 373
     $ ./apsamp 1
     spur = 2236

Results of invocation of the example program in the normal mode of operation using each of the supported schemes and random seeds in the range 1 to 20, and in the completely random mode of operation using seeds in the range -20 to -1 are represented in the table below. Negative seeds for the completely random mode of operation are negated values of the ‘SEED’ column. The ‘RAND’ column contains values of user spur accumulated in the completely random mode of operation.

       SEED RAND S1_S1 S1_S3 S1_L3 S3_S1 S3_S3 S3_L3 L3_L3
     ------ ---- ----- ----- ----- ----- ----- ----- -----
          1  373  2236  3854  4259  1022   824  1872  1735
          2  380   823   900  1867     6   385  3254     5
          3  397  1120     3  2473    46  1120  5452  2289
          4  366  1071   638  3604  1130   713  3502  2266
          5  411  1787  1451  3569  2253  3074  5210  1444
          6  446  3493  3050  1758  5266  2224  5527  5487
          7  430  1295  1940  1780  1882  1656  5169   311
          8  387  3236  4513  1704  2532  4299  2320     3
          9  351     4     2  3214  1501  4085  2071  1141
         10  402  1262  3677     1  2181  3039     3   932
         11  407  3547  1427  1264  1147  1665  2331  1742
         12  428  2925  2650  1173  4013  3777  6665  1608
         13  402  2710  2329  5059   304  3037  6356    15
         14  384  3233  3575  3962  1010  1122  2309  1085
         15  407  1962  1616  5921  3049  4168  3919   953
         16  388  1082  2502  2786  5591  1453   719   270
         17  378  2153  1937  1739  2040  2355  3714  2587
         18  420  1294  1314   697  4813  5828  6463  1472
         19  416     1    39  1454     2  1819  3358   738
         20  392  4681  1633  8205  1691  4489  2666  2216
     ------ ---- ----- ----- ----- ----- ----- ----- -----
        AVG  398  1996  1952  2824  2074  2557  3644  1415
     STDDEV   23  1251  1321  1966  1707  1511  1919  1254
      % EFR  100   502   490   710   521   642   916   356

The ‘AVG’ and ‘STDDEV’ rows contain arithmetic means and standard deviation of column values located above. The ‘% EFR’ row contains values in the ‘AVG’ row multiplied by 100 and divided by the value of a cell at the intersection of the ‘AVG’ row and the ‘RAND’ column. As it can be seen from the table, the average amount of user spur accumulated by the pair of actors for 20 invocations of the example program in the normal mode of operation is about 4–9 times greater than in the completely random mode of operation.


Next: , Previous: apsamp, Up: Example Programs

7.3 optpath

This program performs the search of a path in the state transition network represented in Figure 5.2. The final state of the path is 9. The example program tries to find a path that satisfies two optimization goals: the path should be short, and the path should have maximum geometric mean probability of state transition. To find an optimal path, the example program traverses the state transition network from the initial state to the final state a number of times defined by macro NVISIT (that has value 400).

The example program creates a single-node model that uses two spur types and has a restriction that the action emission matrix must define deterministic choice of an instruction class emitted in every node state. The restriction eliminates the need to specify stt instructions in an assembler program that represents the state transition network. To code the state transition network in the assembler program, an approach is used analogous to one described in Specifying State Transition Networks.

The example program turns off the use of the automatic spur by the environment state identification engine. Instead of the automatic spur (of type 0), the environment state identification engine uses the spur equal to the sum of logarithms of probabilities of state transitions performed. Spur of type 1 countervails spur of type 0 and is incremented by 1 each time the final state is reached. Continuous time tracked by the model is incremented by 1 after every state transition.

At the end of its run, the example program prints the sum of logarithms of probabilities of state transitions performed, the total number of state transitions performed, and the geometric mean probability of state transition. Arguments of incspr instructions in the assembler program specify probabilities of state transitions less than 1. To register instruction classes ‘incspr prob’, the memory representation of the assembler program is scanned, names and parameters of instructions are fetched by function qsmm_instr_str.

A random seed can be specified by a program argument. If the random seed is non-negative, then the program will operate normally. If the random seed is negative, then state transitions will be selected completely randomly. You could compare the program output for these two modes of program execution.

Here is given sample program output.

     $ ./optpath -1
     logprob = -1551.45
     path_len = 2755
     step_prob = 0.569417
     $ ./optpath 1
     logprob = -829.073
     path_len = 1903
     step_prob = 0.646834

Results of invocation of the example program in the normal mode of operation using random seeds in the range 1 to 20, and in the completely random mode of operation using seeds in the range -20 to -1 are represented in the table below.

       SEED  logprob path_len step_prob    SEED  logprob path_len step_prob
     ------ -------- -------- ---------  ------ -------- -------- ---------
         -1 -1551.45     2755  0.569417       1 -829.073     1903  0.646834
         -2 -1580.21     2692  0.555991       2 -894.364     1867  0.619379
         -3 -1590.75     2800  0.566587       3 -871.942     1861  0.625919
         -4 -1628.23     2857  0.565577       4 -827.89      1876  0.643196
         -5 -1661.79     2923  0.566361       5 -828.801     1882  0.64379
         -6 -1582.71     2770  0.564748       6 -839.799     1873  0.638668
         -7 -1552.68     2731  0.566352       7 -824.554     1855  0.641143
         -8 -1619.09     2860  0.567726       8 -823.913     1867  0.643198
         -9 -1606.98     2836  0.567431       9 -835.016     1885  0.642121
        -10 -1540.68     2719  0.567431      10 -848.998     1888  0.637832
        -11 -1605.54     2833  0.567379      11 -869.818     1873  0.628513
        -12 -1596.22     2788  0.564095      12 -792.632     1912  0.660633
        -13 -1524.97     2734  0.572479      13 -820.228     1852  0.642179
        -14 -1577.36     2752  0.563737      14 -845.155     1858  0.634528
        -15 -1623.44     2770  0.556506      15 -751.143     1831  0.663493
        -16 -1579.98     2758  0.563904      16 -805.133     1819  0.642349
        -17 -1570.52     2839  0.575109      17 -784.603     1873  0.657769
        -18 -1577.83     2779  0.566789      18 -807.793     1822  0.641879
        -19 -1555.77     2719  0.564291      19 -815.895     1870  0.646419
        -20 -1497.82     2749  0.579922      20 -877.093     1918  0.632993
     ------ -------- -------- ---------  ------ -------- -------- ---------
        AVG -1581.2    2783.2 [0.566588]    AVG -829.7     1869.3 [0.641554]
     STDDEV    38.2      58.6  0.00534   STDDEV   33.7       26.3  0.010816

The intersections of the ‘AVG’ row and ‘logprob’ and ‘path_len’ columns contain arithmetic means of column values located above. The intersections of the ‘AVG’ row and ‘step_prob’ columns contain geometric mean probabilities of state transitions for 20 invocations of the example program. The ‘STDDEV’ row contains the standard deviation of column values located above.

As it can be seen from the table above, the geometric mean probability of state transition for 20 invocations of the example program for the normal mode of operation is by 0.074966 greater than that probability for the completely random mode of operation.


Next: , Previous: optpath, Up: Example Programs

7.4 test

This program demonstrates basic features of algorithms used in the QSMM package and is implicitly referred from paper “An Approach to Optimal Action Generation for a System that Interacts with the Environment.” In QSMM version 1.16 that paper is removed from the package distribution, because despite being informative, the paper contains serious mistakes, which are not fixed yet. In its current state, the paper can be accessed from the project home page (see Obtaining QSMM, for the project home page URL).

The example program performs interaction between the system and an environment represented by a deterministic finite automaton. The interaction can be performed in a mode when the system is homogeneous and in a mode when the system is comprised of two subsystems, the first of which identifies current environment states and the second generates optimal actions based on those states. An automaton that takes part in the interaction is either read from a file specified by a program argument or generated randomly on the fly.


Next: , Up: test

Format of Automaton File

A deterministic finite automaton that models the environment is described in a text file, an example of which is shown below.

     Non-empty line #1
     Non-empty line #2
            ...
     Non-empty line #n
     
     2 5 3 0
     1 0 0 0 0
     1/3 2/4
     2/1 1/2
     0/4 1/0

All lines before the first empty line are ignored. The first line after that empty line has format

     number_of_input_signals number_of_output_signals number_of_states initial_state_index

Indices of signals and states start from 0.

The second line after the empty line contains floating-point numbers, which are the increments in system spur for all output signals. Those increments take place when corresponding output signals are emitted by the automaton.

Starting from the third line after the empty line, there go descriptions of transitions between states upon receiving input signals. The third line corresponds to transitions from state 0, the fourth—from state 1, the fifth—from state 2, and so on. Each line contains number pairs that correspond to input signals. The first pair corresponds to input signal 0, the second—to input signal 1, the third—to input signal 2, and so on. A pair has format

     target_state/output_signal

and specifies a transition from a source state to a target state triggered by an input signal, during which an output signal is emitted.


Next: , Previous: Format of Automaton File, Up: test

Generating Random Automatons

To generate a random deterministic finite automaton, the following command can be used:

     $ ./test -o out_file -i random_seed [ -C VAL ] num_in_sig num_out_sig num_states

The command creates file out_file with a description of deterministic finite automaton produced using seed random_seed. The automaton has given numbers of input, output signals, and states. A random seed should always be specified; otherwise some standard fixed value will be used. The first output signal will have spur increment 1 and all others 0.

If option -C VAL with non-zero VAL is specified, then the automaton will have its state graph connected. If VAL is a number greater than 0, then the automaton will have not more than that number of cycles in the graph, and the best cycle among them will be found. If the number is relatively small, then the automaton may not be generated at one push. When an attempt to generate the automaton fails, a warning message is printed, and the program makes another attempt. If the number is relatively big, then an attempt to generate the automaton may take a long time, so the impression can be that the program has hung.

In QSMM version 1.16, there are supported VAL=‘c’ and VAL=‘cs’. Value ‘c’ makes the program to provide state connectivity of the state graph and to find the best cycle in the graph using an algorithm analogous to Viterbi one. Value ‘cs’ makes the program to provide the state connectivity, simplify the state graph, as it performed when VAL is a number greater than 0, and find the best cycle in the graph using an algorithm analogous to Viterbi one.

To comment lines before the first empty line in out_file the value of random seed is written. If option -C VAL with non-zero VAL is specified, then to comment lines will also be written information on the best cycle in the state graph:


Next: , Previous: Generating Random Automatons, Up: test

Performing the Interaction

A test of algorithm efficiency that consists of num_passes passes with a new random deterministic finite automaton, which has defined numbers of input, output signals, and states, being chosen at each pass, is performed by command

     $ ./test -t num_passes [ -C VAL ] [ additional options ]  \
       num_in_sig num_out_sig num_states

If option -C VAL with non-zero VAL is specified, then best cycles will be found in automaton state graphs which will be provided to be connected ones. The command prints a test log, an example of which is shown below.

     $ ./test -t10 -Ccs 20 20 20
         DFA inputs: 20
        DFA outputs: 20
         DFA states: 20
      Input signals: dfa-state
             Passes: 10
     Steps per pass: 10000
              Large: off
      R. prob. type: 1
      N-gram length: 1
      Algorithms v.: 0
            K*temp.: 1.000000000000000E+00
        Max. cycles: -2
        Random seed: 0
     
     pass       earned     random      maximal cl    % efr   % efa
     ---- ------------ ---------- ------------ -- -------- -------
        1     5821.000    331.000     7500.000  4 1758.610  76.580
        2     5826.000    441.000    10000.000  2 1321.088  56.334
        3     5656.000    359.000     6666.667  3 1575.487  83.977
        4     6443.000    414.000    10000.000  2 1556.280  62.894
        5     7075.000    462.000    10000.000  2 1531.385  69.333
        6     5949.000    329.000     7142.857  7 1808.207  82.479
        7     4272.000    281.000     6666.667  3 1520.285  62.499
        8     8781.000    411.000    10000.000  2 2136.496  87.288
        9     8144.000    648.000    10000.000  2 1256.790  80.154
       10     4671.000    270.000     6000.000  5 1730.000  76.806
     
     TOTL    62638.000   3946.000    83976.190  3 1587.380  73.337
     
     stddev efr:  253.554
     stddev efa:   10.486

The log begins with a description of test modes used. Then there goes a table, where each row corresponds to a test pass, with a summary row. The columns of the table have the following meaning.

pass
A test pass index. For the summary row ‘TOTL’ is printed here.
earned
The amount of spur the system has received during interaction with the automaton according to the algorithm. In the summary row—the sum of values in the column.
random
The amount of spur the system has received during random interaction with the automaton when output signals are equiprobably chosen from the set of allowed signals. In the summary row—the sum of values in the column.
maximal
The amount of spur the system would receive during the most optimal interaction with the automaton. Will be determined if option -C VAL with non-zero VAL is specified in the command line, otherwise will be equal to the number of steps at a test pass. In the summary row—the sum of values in the column.
cl
The length of a cycle, continuous repeating of which would give the maximum amount of spur to the system. Will be determined if option -C VAL with non-zero VAL is specified in the command line, otherwise will be equal to 0. In the summary row—the average length of cycles for all test passes.
% efr
Relative efficiency equal to (earned/random)*100%. For the summary row is calculated using corresponding values in that row.
% efa
Actual efficiency equal to (earned-random)/(maximal-random)*100%. For the summary row is calculated using corresponding values in that row.

A standard deviation of values in ‘% efr’ and ‘% efa’ columns is printed at the end of the log.


Next: , Previous: Performing the Interaction, Up: test

Running Multiple Test Passes for a Single Automaton

To run certain number of test passes for an automaton specified in FILE, which has a format described in subsection Format of Automaton File, the following command can be used:

     $ ./test -t num_passes [ -C VAL ] [ additional options ] -f FILE

The automaton is checked for having its state graph connected. If VAL is a number greater than 0, then the automaton will also be checked for having the number of cycles in the graph less than or equal to VAL. If VAL is non-zero, then the test log will be prepended with information on the best cycle in the automaton state graph as it described in subsection Generating Random Automatons.


Next: , Previous: Running Multiple Test Passes for a Single Automaton, Up: test

Options Common for Both System Types

Options of the program that can be used in both cases, when the system is homogeneous and when the system is comprised of two subsystems, the first of which is an environment state identification subsystem and the second of which is an optimal action generation subsystem, are listed below.

-C, --ncycle-max=VAL
Parameters related to automaton state graphs: state connectivity, simplification, the maximum number of cycles, and finding the best cycle. In the description of test modes is denoted by ‘Max. cycles’. Supported values are described below.
0
Do not provide the state connectivity, do not perform the simplification, do not restrict the maximum number of cycles in the graph, and do not find the best cycle.
>0
Provide the state connectivity, perform the simplification, restrict the maximum number of cycles in the graph by VAL, and find the best cycle.
“c”
[New in QSMM 1.16] Provide the state connectivity and find the best cycle by the algorithm analogous to Viterbi one. In the description of test modes is denoted by value -1.
“cs”
[New in QSMM 1.16] Provide the state connectivity, perform the simplification, and find the best cycle by the algorithm analogous to Viterbi one. In the description of test modes is denoted by value -2.

The simplification of an automaton state graph consists in replacing transitions from (source) states to other states with transitions to the same (source) states on condition that resulting transitions do not yield positive spur increments, and the connectivity of the state graph is preserved.

If search of the best cycle in the state graph is performed, then the maximum amount of spur the system would receive during the most optimal interaction with the automaton will be calculated and printed in the ‘maximal’ column of test log.

-f FILE
A file to read the automaton from. In the description of test modes is denoted by ‘Automaton file’.
-i, --seed=INT
A seed to initialize the pseudorandom number generator. Default value is 0. In the description of test modes is denoted by ‘Random seed’.
--kt=FLOAT
The temperature (multiplied by some constant) of the homogeneous system or the temperature of the environment state identification subsystem and the optimal action generation subsystem in case when the system is comprised of two subsystems. Default value is 1. In the description of test modes is denoted by ‘K*temp.’.
-n, --nstep-pass=INT
The number of steps of interaction with a deterministic finite automaton at each test pass. At each step, the automaton receives an input signal and emits an output signal. Default value is 10000. In the description of test modes is denoted by ‘Steps per pass’.
-o FILE
A file to write program output to instead of stdout.
-S, --storage=flat|map
A statistics storage type for the system.
flat
Use preallocated storage presumably of big size, but with quick access to data elements. Is default mode when possible.
map
Use dynamically allocated storage presumably of lesser size, but with slower access to data elements. The implementation of functionality of the STL map template is used for backing