Installing Models

This Concept Guide introduces installed Rel models, which can be reused later in queries or as a building block for other models.

Introduction

RAI databases are collections of (1) relational data and (2) definitions and rules that define a model that operates on that data.

Simple definitions are often expressed as part of the query, and forgotten afterwards. For example, the following query computes the sum of the numbers from 1 to 10, putting them in a myrange relation and then asking for sum[myrange]:

def myrange(x) = range(1, 10, 1, x)
def output = sum[myrange]

Relation: output

55

The relation myrange is an IDB relation. When this code is issued as a query, it is not persisted in the database, and its definition is forgotten once the query is finished. This is convenient for simple auxiliary relations that are only used once.

For more advanced applications, we want to persist relation definitions in the database so they can be reused later in queries, or as building blocks for more complex models. We call this step installing models. In this way, complex applications can be constructed in a modular way, as a collection of Rel libraries.

Installed Rel models are identified by a user-defined name. They can be added, removed, or updated, as we will see below.

Simple Example

Let’s install a small Rel model source that defines the transformation from Cartesian to spherical coordinates. We also define a (singleton) relation $\pi$ that holds the value of pi, taking advantage of Rel’s support for Unicode characters:

def π = pi_float64

@inline
def cartesian2spherical[R] = r, phi :
r = sqrt[x^2 + y^2],
phi = atan2[y, x] / π * 180
from x, y where R(x, y)

The relation cartesian2spherical accepts an arity-2 relation R that holds the Cartesian coordinate pairs (x, y) and returns a pair (r, phi) which is the radius and the angle of the corresponding 2D-point. Now that you have installed this definition in the database, you can use it in a query:

def xy = {("a", 2, 0); ("b", 0, 4); ("c", -9, 0); ("d", 1, -1)}
def r_phi[i] = cartesian2spherical[xy[i]]

def output = r_phi

Relation: output

"a"2.00.0
"b"4.090.0
"c"9.0180.0
"d"1.4142135623730951-45.0

Installing Integrity Constraints

You call also install integrity constraints (ICs). The installed ICs will ensure that the database will be always in a state that fulfills the installed IC. Please read the Integrity Constraints concept guide for more on ICs.

For example, let’s install an IC that makes sure that all of the elements of the myrel transaction are positive — along with an initial set of values for myrel itself:

def myrel = {1; 2; 3}

ic myrel_positive(x) {
myrel(x) implies x > 0
}

The integrity constraint passes (everything in myrel is a positive number), and both the IC and the definition are successfully installed.

myrel

Relation: output

1
2
3

Impact on Future Transactions

Installing Models

If you now try to extend the definition of myrel by installing another definition for myrel that also includes negative numbers, then the install attempt fails and the definition for myrel will not change:

failed install
def myrel = {-1; 10}

def name = {"John"; "Jane"}

Any other definitions that you may have tried to install, such as name above, will not succeed and the installation of the entire code block will fail. You can check that myrel has not changed:

myrel

Relation: output

1
2
3

Querying the Database

Query transactions that introduce temporarily definitions that violate installed ICs will fail. The system will let you know that the IC was violated, and no results are returned. The error message may contain the content of the requested relations, and details of the IC violation. An exception is the RAI notebook which returns the result whether or not the IC is failed.

failed query
def myrel = -1

def output = myrel

EDB updates, which are queries that can modify the database state, will also fail if an IC is violated, and the requested EDB change (via insert or delete) will not be persisted in the database.

To demonstrate this behavior, try to expand myrel again by adding -1, and also try to create a new EDB relation called name_edb:

failed update
def myrel = -1.0

def insert:name_edb = {"John"; "Jane"}
def output = name_edb

Let’s see if the relation name_edb is persisted in the database:

def output = name_edb

Relation: output

You can see that name_edb is still not defined, and the output is empty. The requested insertion into name_edb was not persisted in the database because of the IC violation.

ICs with Yet-To-Be Defined Relations

To install an integrity constraint that refers to an IDB relation that not yet been defined, you can assign the empty relation {} (aka false) to it. Since multiple definitions for the same relation are unioned, this does not affect the values in the relation that might be assigned later. For example:

def my_data = {}

ic my_data_ic(x) {
my_data(x) implies Int(x)
}

def my_data = {} ensures that my_data is defined, and no error message is issued.

RAI Notebooks

Installed models are marked as install cells in the RAI Notebook. Install cells are given a unique system-generated ID by default. This ID can be replaced by a user-provided one. See Guide to Working with RAI Notebooks for more details.

Normal query cells extract information (and don’t change the database) transactions. Cells marked as update cells can change EDB relations, but don’t install new sources. See the concept guide on Updating Data: Working with EDB Relations for more details.

Model Introspection

Installed models are themselves stored in an EDB relation, which allows Rel programs to query and modify them.

This relation, available as rel:catalog:model, can be used to query, update or delete model sources themselves.

For example:

def insert:rel:catalog:model["My Rel Model"] = """
def myrange = range[1,100,1]
"""
def output:stats = sum[myrange], max[myrange]
def insert[:foo] = myrange
def output:foo = foo <++ "undefined"

Relation: output

:foo"undefined"
:stats5050100

This code has inserted a definition for myrange, and was able to query it in the very same transaction.

However, myrange was not yet available for updating the EDB relation foo, since insert and delete statements, unlike outputs, are evaluated at the start of the transaction, while outputs are evaluated at the end. (See the Updating Data Concept Guide for more details.)

We can list the names of all the installed model sources, which include the Rel libraries, as follows:

def output(name) = rel:catalog:model(name, _)

Relation: output

"My Rel Model"
"alglib"
"display"
"histogram"
"ic_example"
"intrinsics"
"ml"
"my_data_ic"
"rml"
"spherical"
"stdlib"
"vega"
"vegalite"

In RAI Notebooks, the names of the installed models in the notebook will have the form "notebooks/<notebook_name>/<install_cell_ID>".

To see the content of a installed model, you can query rel:catalog:model:

def output = rel:catalog:model["My Rel Model"]

Relation: output

" def myrange = range[1,100,1] "

This shows us the code tagged "My Rel Model", which was installed above.

You can delete this model with:

def delete:rel:catalog:model["My Rel Model"] = rel:catalog:model["My Rel Model"]

You can similarly delete any of the other installed models.

See the RAI article on “Rel: Live Programming and Self-Modifying Models”, for more on the self-modifying aspects of Rel.