Installing Models
This concept guide introduces installed Rel models, which can be reused later in queries or as building blocks for other models.
Introduction
RAI databases are collections of:
- Relational data.
- 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]
:
// query
def myrange(x) = range(1, 10, 1, x)
def output = sum[myrange]
The relation myrange
is a derived 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, you want to persist relation definitions in the database so they can be reused later in queries or as building blocks for more complex models. This step is called 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 you will see below.
Simple Example
Start by installing a small Rel model source that defines the transformation from Cartesian to spherical coordinates. You also define a relation , which holds the value of pi, taking advantage of Rel’s support for Unicode characters. This is a singleton relation, which means it has cardinality 1 and arity 1. Here’s what the code looks like:
// install
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 above code is meant to trigger an install_model
RAI SDK API call. This is indicated by the “install” annotation above the code block.
In RAI Notebooks, installing models is done by an install cell.
The relation cartesian2spherical
accepts an arity-2 relation R
, which holds the Cartesian coordinate pairs (x, y)
, and evaluates 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 like this:
// 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
Installing Integrity Constraints
You can also install integrity constraints (ICs). The installed ICs will ensure that the database will always be in a state that fulfills the installed IC. For more information on ICs, see the Integrity Constraints concept guide.
For example, 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:
// install
def myrel = {1; 2; 3}
ic myrel_positive(x) {
myrel(x) implies x > 0
}
The integrity constraint passes, since everything in myrel
is a positive number, and both the IC and the definition are successfully installed.
// query
myrel
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:
// install
def myrel = {-1; 10}
def name = {"John"; "Jane"}
Any other definitions that you may have tried to install, such as name
, will not succeed, and the installation of the entire code block will fail.
Here’s how to check that myrel
has not changed:
// query
myrel
Querying the Database
Query transactions that introduce definitions that violate installed ICs will fail. The system will let you know that the IC was violated, and no results are evaluated. The error message may contain the content of the requested relations and details of the IC violation. An exception is the RAI notebook; it evaluates the result, whether or not the IC fails. Here’s an example:
// query
def myrel = -1
def output = myrel
Base relation updates, which are queries that can modify the database state, will also fail if an IC is violated.
The requested base relation 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 create a new base relation called name_base_relation
:
// update
def myrel = -1.0
def insert:name_base_relation = {"John"; "Jane"}
def output = name_base_relation
To check if the insertion was successful, query name_base_relation
:
def output = name_base_relation
name_base_relation
is still not defined, and the output is empty.
The requested insertion into name_base_relation
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 a derived relation that has not yet been defined,
you can assign the empty relation {}
to it.
This does not affect the values in the relation that might be assigned later because all definitions for the same relation form a union.
Here’s an example:
// install
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 the Guide to Working with RAI Notebooks for more details.
Normal query cells extract information transactions without changing the database transactions. Cells marked as update cells can change base relations, but they don’t install new sources. See the concept guide on Updating Data: Working with Base Relations for more details.
Model Introspection
Installed models are themselves stored in a base 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 the model sources themselves.
Here’s an example:
// update
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"
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 base 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 Data Concept Guide
for more details.
Here’s how to list the names of all the installed model sources, which include the Rel libraries:
// query
def output(name) = rel:catalog:model(name, _)
In RAI notebooks, the names of the installed models in the notebook will have the following form:
"notebooks/<notebook_name>/<install_cell_ID>"
.
To see the content of an installed model, you can query rel:catalog:model
like this:
// query
def output = rel:catalog:model["my-rel-model"]
This shows the code tagged "my-rel-model"
, which was installed above.
Here’s how to delete this model:
// update
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.