# Predicates & Constraints

In Penrose, we are not only given the power to represent mathematical objects with shapes, but we are also able to represent complicated relationships between the objects. In this tutorial, we will learn about defining **predicates**, and visually representing them with the constraint keyword `ensure`

. After this tutorial, you should be equipped to create diagrams with relationships between objects in Penrose.

The starter code for this tutorial can be found in the `./packages/examples/src/tutorials/code/tutorial2`

folder of the `penrose`

repo you cloned in part 1.

## Goal

In the second example, we will work to diagram a set that is a **subset** of another, i.e. "A is a subset of B". Recall that A is a subset of B if and only if all the elements in A are in B. For those who are not familiar with subsets, you can think of apples as a subset of fruits, because every apple is a fruit. In diagrams, the subset relationship is commonly represented by drawing A as a smaller circle that is fully contained in the circle that represents B.

## 📄 Domain

To illustrate the subset relationship, we have to expand our domain file to let Penrose know that an arbitrary set can be a subset of another arbitrary set. Recall that in Tutorial 1 we defined `Set`

objects using `type Set`

, and now we want to define a * relationship* between two sets.

To define a relationship between objects in the domain, there are a few things we need to decide on:

- The name of the relationship
- The number of arguments, i.e. how many objects are involved in this relationship
- The type of arguments, i.e. what are the type of objects that are involved in this relationship

The syntax for declaring a relationship is through the use of keyword `predicate`

, the name of the predicate, and the objects that are involved in the predicate:

`predicate IsR(t1 var1, t2 var2)`

(Here, `t1`

is the type of `var1`

, and `t2`

is the type of `var2`

.)

This pattern informs Penrose that there is a relationship that we care about called `IsR`

which takes arguments of type `t1`

and `t2`

. Penrose still doesn't know how to visually represent this relationship, so we will have to define that logic in our `.sty`

file a little later.

In the case of our current example, we can name our relationship `IsSubset`

, and we have 2 arguments of type `Set`

.

`type Set`

predicate IsSubset(Set s1, Set s2)

Now we are free to use the predicate `IsSubset`

in our `.sub`

file and define what it means visually in our `.sty`

file.

## 📄 Substance

In our goal diagram, we have 3 sets, therefore we will declare 3 different sets in our `.sub`

file. Note that in the previous example we declared each of our sets on separate lines, but we could also declare multiple objects of the same type in a single line. For instance, `Set A, B, C`

is equivalent to writing:

`Set A`

Set B

Set C

So, we declare the set objects that will appear in our diagram, then we declare the relationships between the sets. In this case, let's make "B a subset of A" and "C a subset of B".

`Set A, B, C`

IsSubset(B, A)

IsSubset(C, B)

## 📄 Style

The style program will be the most complex part, and you find that this is expected when developing with Penrose. In this example, we introduce a new keyword `ensure`

, which allows you to constrain certain aspects of certain shapes. Essentially, we use `ensure`

to let Penrose know that these are rules the diagram **must** satisfy. Hence, we also call `ensure`

statements *constraints*.

Recall that we learned about predicates that are defined in `.dsl`

and used in `.sub`

, and now we need to define the visual definition of the predicate. To show that "A is a subset of B", we need the following visual characteristics:

- A's circle needs to be smaller than B's circle
- A's circle needs to be contained within B's circle (their borders should not be intersecting in any way)
- A's circle is on top of B's circle

Therefore we call the corresponding `ensure`

functions on the `.icon`

fields (that we used to assign shape objects to in tutorial 1) of two arbitrary sets that have the relationship of `IsSubset`

.

Now our selector is not just `forall Set A`

since we only want to apply these styling rules to the sets that have the relationship `isSubset`

. Therefore, we need to add a condition to the arbitrary sets we are looping through in the program. We can do this with the pattern `where HasRelationshipR(A, B)`

where the `HasRelationshipR`

is `IsSubset`

in this particular case. Now our `.sty`

file looks like this:

`forall Set A; Set B`

where IsSubset(A, B) {

ensure smallerThan(A.icon, B.icon)

ensure contains(A.icon, B.icon, 5.0)

A.icon above B.icon

}

`smallerThan()`

and `contains()`

are a couple of the built-in constraints pre-defined by Penrose. A full list of available constraints is available in the `constrDict`

object documentation here.

Notice that in our first example, we did not care about the size of our shapes, but now we want to maintain a specific hierarchy between objects. While Penrose tries to satisfy all constraints, it is possible for the circles to become too big or too small. So we need to specify a range of acceptable sizes for our circles so nothing goes crazy.

Since we care about the sizes of **all** the sets and we need to **ensure** all of their sizes are within a reasonable range, we will again make use of our newly introduced keyword `ensure`

. We call `ensure`

on any fields of the object that we want to limit to within a certain range. In this case, we want to constrain the size of the shapes, so we add a call to `ensure minSize(x.icon)`

. `minSize`

is another pre-defined constraint that Penrose provides. If you're curious, the documentation for this constraint can be found here (scroll down to `minSize`

). You can also see how Penrose implemented this constraint by clicking on the "Defined in" link.

`forall Set x {`

x.icon = Circle {

strokeWidth : 0.0

}

ensure minSize(x.icon)

}

So putting it all together, we have:

`forall Set A; Set B`

where IsSubset(A, B) {

ensure smallerThan(A.icon, B.icon)

ensure contains(A.icon, B.icon, 5.0)

A.icon above B.icon

}

forall Set x {

x.icon = Circle {

strokeWidth : 0.0

}

ensure minSize(x.icon)

}

To compile our new code, we can run the same commands as last time, with one minor change. Since the files we are working with are in the `tutorial2`

folder instead of the `tutorial1`

folder, we have to change our `<path-to-triple>`

. So, we build as follows:

- Open two terminals at the root of the Penrose repository.
- Run
`yarn start`

from the first terminal window - Open your browser at
`localhost:3000`

. - From the root of the Penrose repository, run:
`npx roger watch <path-to-triple>/subset.sub <path-to-triple-repo>/subset.sty <path-to-triple>/setTheory.dsl`

- Refresh your browser window!

## Exercises

Complete the following exercises to practice implementing predicates in Penrose! As a reminder, you can find the documentation for all pre-defined constraints here.

- Define a predicate
`Intersecting`

that takes in two sets and outputs 2 circles that overlap.

- Define a predicate that takes in two sets and outputs 2 circles that are disjoint.

## Take-aways

- We use the keyword
`predicate`

to define relationships between objects. - We use the keyword
`ensure`

to define constraints on the diagram.