NAV Navbar

Inkling Overview

#######################################################
# Inkling Code and Syntax snippets will be shown here #
#######################################################

This reference contains comprehensive specifications and usage for the Inkling language, Bonsai’s special purpose programming language for training AI.

Inkling is a declarative, strongly typed language that provides a layer of abstraction between the Inkling programmer and the vast and dynamic set of AI algorithms that require expertise in machine learning. Inkling allows you to focus at a conceptual level on what you want the machine to learn. That is why this model of programming is called machine teaching.

An Inkling file contains the concepts (what you want to teach the AI), a set of schemas (which describe the data) and the curriculums (how you want to teach the concepts) necessary to train your BRAIN. Training your BRAIN will also require a simulator. Python simulators are introduced in the Quick Start.

Purpose

The purpose of the Inkling Reference is to give you the level of detailed specification you need to write valid Inkling code. The syntax for Inkling statements is available here. This document also contains examples and explanation so that you can understand Inkling better.

Functionality

Use the Inkling Reference for quick lookups while coding and also for deep dives into subjects like schema conformance and range expressions.

Lexical Structure

The lexical structure of Inkling includes these lexical elements:

  • keyword: the keyword set consists of the words that Inkling reserves for its own use.
  • identifier: user defined names in Inkling. For example, concept names.
  • literal: Inkling supports numeric and string literals.
  • operator: Inkling supports operators such as math operators and grouping operators.
  • comment: specifies comment format.

Keywords

Here is the set of keywords in the Inkling language. These words are reserved for use by Inkling and cannot be used as names in your program.

Keywords Table

action and as Bool
Byte concept configure constrain
copy curriculum data datastore
debug Double easy end
expect experimental external false
feeds Float32 Float64 follows
format from generator hard
import in input Int16
Int32 Int64 Int8 interface
into is lesson let
Luminance Matrix maximize medium
minimize not objective or
output predicts schema select
send simulator state stream
String test train true
UInt16 UInt32 UInt64 UInt8
unit until using validate
where with yield

Identifiers

    1st_name        # invalid identifier
    -primary-key    # invalid identifier
    remember_me?    # invalid identifier

An Inkling identifier (user defined name) must begin with an underscore or letter, followed by any combination of alphanumeric characters and underscore.

Literals

Inkling supports numeric literals (floating point and integer).

   +999
   -3
    77
  • Integer Literals

Integer literals are a string of digits with an optional sign and no decimal point.

 12.0, .5        # Float32 floating point literal
 1e7, 9e0        # Float64 (double) floating point literal
 13.0f7, .3f+2   # Float32 floating point literal
  • Floating Point Literals

Floating point literals can be Float32 or Float64 (double). An example is shown for some floating point literals.

Operators

The operator category includes mathematical, relational, and logical operators as well as paired grouping operators like { and }.

Operators Table

<= < == => = >=
- , : != / ..
( ) [ ] { }
+ and not or

Comments

  # this is a comment
  • An Inkling comment begins after the character # and extends to the end of the line.

Concepts

The concept keyword declares an abstract concept that is to be learned by the system. It could be a feature (such as a curvy line in an image) or a goal (such as high score in a game). Ultimately, a concept takes the form of a transformation of data, but no information need be provided about how to perform the calculation. By declaring a concept, you are instructing the BRAIN server that this is a node in the basic recurrent artificial intelligence network that must be learned. Consequently, concept nodes must have corresponding curricula to teach them.

concept AbstractConceptName
  is classifier
  predicts ConceptSchema
  follows Antecedent1, Antecedent2   
  feeds Dependent1                 
end

Because concepts are learned, their declarations tend to be fairly simple. Inkling will support the ability to explicitly tell the BRAIN server what learning algorithms and architecture to use but this is not yet implemented. (It is an unusual case).

The typical components of a concept statement are shown in the accompanying panel.

Concept Syntax

conceptStatement :=
concept
  is [ classifier | estimator ]
  predicts ( schemaRef )
  [
    follows
      inputSource [',' inputSource ]*
    ]?
  [
    feeds
      outputTarget [',' outputTarget ]*

  ]?
end

inputSource ::=
    input '(' schemaRef? ')' | <name>     # name of a concept or input

outputTarget ::=
    output |  <name>                      # name of a concept or output

The is keyword specifies the overall class of concept that is being modeled. For example, a classifier will learn to identify a label corresponding to its input, an estimator will learn to predict a value, and a predictor (not yet implemented) will learn sequences and predict subsequent items in a sequence.

The predicts keyword declares the concept’s output.

The follows and feeds keywords establish connectivity in the BRAIN directed graph.

The follows, feeds, and predicts clauses can be in any order.

Like all Inkling toplevel statements, the end keyword declares the end of the statement.

Usage

The concept statement specifies input sources and output targets. Input sources are listed after the follows keyword and output sources are listed after the feeds keyword.

concept bar is classifier
  predicts (Move)
  # feeds foo            not necessary 
  follows input(UInt8 x)
end

concept foo is classifier
  predicts (Action)
  follows bar     
  feeds output
end

Note that follows and feeds can be redundant. If our concept foo follows our concept bar then we know that bar is an input source for foo, and it isn’t necessary to also specify that concept bar feeds concept foo. Specifying foo follows bar is enough. However, specifying both (foo follows bar and bar feeds foo) is valid.

Input sources can be other concepts or the input stream. The input stream is the original input to the system. It flows into the system from outside the BRAIN. Each reference to the input stream must have a schema, which can be an anonymous schema.

Output targets can also be other concepts or the output stream. The output stream refers to the output of the BRAIN. The output stream is never referenced with a schema.

The concept output is described in the predicts clause. The schema reference after the predicts keyword describes the data produced by the trained concept. (This schema reference is required but it can be anonymous.) For example, if this concept classifies email into spam and not spam, the output schema for the concept would be a Bool.

The is clause characterizes the output. The is classifier form specifies that the output is an enum. The is estimator form specifies that the output is a value.

Concept input can come from a concept as well as the input stream.

When input comes from a concept, the type of the input doesn’t matter. This is because concepts don’t act on normal data science data structures. The concept input is a matrix which has no type.

Concepts do care about their input types when input comes from a stream. This is because the input needs to go through an encoder to become a tensor and the encoder must know the input types. In this case the input types are defined by the output schema of the stream feeding the concept.

The predicts output of a concept is also a matrix which has no type (a tensor). The predicts output schema represents the typed output of the decoder for that output matrix.

Examples

We show Inkling for the concepts get_high_score, Digit, Curvature, and Segments.

get_high_score

concept get_high_score
  is classifier
  predicts PlayerMove
  follows input(GameState)
  feeds output
end
  • conceptName: get_high_score
  • is: classifier
  • predicts: PlayerMove
  • input(schemaName): input(GameState)
  • feeds: output

Digit

concept Digit
  is classifier
  predicts MNIST_output
  follows Curvature, Segments, input(MNIST_input)
end
  • conceptName: Digit
  • is: classifier
  • predicts: MNIST_output
  • follows: Curvature and Segments are concepts and input(MNIST_input) is the input stream with the MNIST_input schema

Curvature

concept Curvature
  is classifier
  predicts (curve_output)
  follows input(MNIST_input)
end
  • conceptName: Curvature
  • is: classifier
  • predicts: curve_output
  • follows: input(MNIST_input)

Note that Digit specifies Curvature in its follows clause. That means logically that Curvature feeds Digit. But specifying this relationship in one place is enough. There would be nothing wrong with adding a feeds Digit clause to Curvature but it isn’t required.

Segments

concept Segments
  is classifier
  predicts (segments_output)
  follows input(MNIST_input)
end
  • conceptName: Segments
  • is: classifier
  • predicts: segments_output
  • follows: input(MNIST_input)

Curriculums

curriculum MyCurriculum
  train MyConcept
  with simulator MySimulator
  objective MyObjective
    # lessons are specified here.
end

The curriculum statement is used to specify the training of Inkling concepts.

The curriculum specifies the concept which is being taught. The lessons defined within the curriculum are used to train that concept.

Curriculum Syntax

curriculum <name>                            
train  <conceptName>                        
[ 
  with simulator <simulatorName>  
  objective <objectiveFunctionName> 
]+ 
[
  using <simulatorName>         # using clause
  [
    lessonClause                # lesson set for this simulator
  ]+
  end
]+
end

Usage

There can be only one curriculum per concept.

Every concept must have a curriculum.

Every simulator must be declared with a simulator clause.

The train keyword indicates which concept this curriculum trains.

The objective keyword specifies the objective function. This function specifies the termination condition for training. It is always required.

If the curriculum uses one simulator, the using simulator clause is optional. Currently, only one simulator per curriculum is supported, but support for multiple simulators per curriculum is anticipated. In that case, for more than one simulator, the using simulator clause is required, to associate a lesson set with a specified simulator. When there are multiple simulators per curriculum, there must be one using simulator clause for every simulator specified in the with simulator clause.

Discussion

The concept graph is laid out by the architect and then handed to the instructor. The instructor will look for the starting point among the concepts and their corresponding curriculums, and then will use the curriculum to train the concept.

A curriculum (and its concept) is associated with a simulator and that simulator has an objective function and a schema. The instructor trains each concept using a simulator with the specified objective function. (Support for multiple simulators per curriculum is anticipated.)

The lesson clause configures the lesson and describes training and the objective. For more information see the section on lessons.

Breakout Example

Breakout Example

simulator breakout_simulator(BreakoutConfig) 
  action  (PlayerMove)
  state  (GameState)
end

The simulator clause declares the simulator name and several schemas. The first schema specifies the schema for configuration of the simulator and it appears in parentheses immediately after the simulator name. In this instance, the configuration schema is named BreakoutConfig. In the example, the configure clauses of the lessons initializes the configuration schema fields of BreakoutConfig.

schema BreakoutConfig
    UInt16 level,
    UInt8{1:4} paddle_width,
    Float32 bricks_percent
end

schema GameState                # state schema
    Luminance(84, 336) pixels
end

curriculum high_score_curriculum
  train high_score
  with simulator breakout_simulator
  objective score
    lesson score_lesson
      configure
        constrain bricks_percent with Float32{1.0},
        constrain level with UInt16{1:100},
        constrain paddle_width with UInt8{1:4}
      until
        maximize score
end

The names in the configuration schema BreakoutConfig are the names referenced in the configure clause of the lessons. When a lesson is initiated, the configuration data as described in the configuration schema is sent to the simulator. The configuration data will be generated according to the range expression in the lesson configure clause for a field.

The second schema specified in the simulator clause is the state schema. It is specified after the state keyword in the simulator clause. This is the schema that defines what is sent to the lesson. Recall that a simulator has state. That means that input to the lesson will consist of the state of the game as a result of the previous lesson execution. For Breakout this schema is called GameState. The Luminance field in GameState describes the state of play in pixels.

The simulator state schema must match the schema associated with input for the system. This reflects the fact that the state is the input to the simulator for selection of the next move.

# predict schema for high_score and action schema for simulator
schema PlayerMove               # action schema
    Int8{-1, 0, 1} move         # possible game moves
end

concept high_score is classifier
  predicts (PlayerMove)
  follows keep_paddle_under_ball, input(GameState)
  feeds output
end

The action schema (which is PlayerMove) is the third schema specified in the simulator clause. The simulator action schema must match the predicts schema of the concept being trained. In our example the concept high_score trains the BRAIN to select the next move, which will have one of the values specified in the PlayerMove schema range expression.

concept keep_paddle_under_ball is classifier
  predicts (PlayerMove)
  follows input(GameState)
end

curriculum keep_paddle_under_ball_curriculum
  train keep_paddle_under_ball
  with simulator breakout_simulator
  objective ball_paddle_distance
    lesson track_ball_any_paddle
      configure
        constrain bricks_percent with Float32{1.0},
        constrain level with UInt16{1:100},
        constrain paddle_width with UInt8{1:4}
      until
        maximize ball_paddle_closeness
    lesson track_ball_wide_paddle
      configure
        constrain bricks_percent with Float32{1.0},
        constrain level with UInt16{1:100},
        constrain paddle_width with UInt8{4}
      until
        maximize ball_paddle_closeness
end

One other concept which helps with playing Breakout is keep_paddle_under_ball. In the curriculum for this concept, there are two lessons. One of the lessons, track_ball_any_paddle, uses a range expression to vary paddle width from 1 to 4. The other lesson, track_ball_wide_paddle, has a fixed paddle width of 4. In training this concept, the fixed wide width paddle lesson is easier to train, and that one would be trained first, resulting in faster and more effective training time overall.

Lessons

Lesson syntax and semantics can vary slightly depending on the curriculum training type. A curriculum specifies its training type by specifying that it trains with a simulator, with data, or with a generator.

Lesson Syntax

lessonStatement ::=
  lesson <lessonName>
    followsClause?
    configureClause
    trainClause?
    testClause?
    untilClause

The lesson declares an individual lesson for the concept being trained by the curriculum. Lessons are contained within curriculum statements. A curriculum can contain multiple lessons.

Lessons provide control over the training of the mental model. They allow the training of the concept to be broken down into phases where each phase is implemented by a lesson. Lessons allow the machine to learn the concept in stages rather than all at once.

Usage

The configure and the until clauses are required.

The train and test clauses are optional.

Lessons can be ordered, using the follows clause. Note that this ordering is a suggestion to the instructor, not a hard and fast rule. If there is no follows clause and the lessons are executed in parallel, training will be slower.

Example

simulator breakout_simulator(BreakoutConfig)
  action  (PlayerMove)
  state (GameState)
end

schema GameState
  Luminance(84, 336) pixels
end

schema PlayerMove
  Int8{-1, 0, 1} move
end

schema BreakoutConfig # configured in lesson configureClause
  Int32 level,
  Int8{1:4} paddle_width,
  Float32 bricks_percent
end

concept high_score is classifier
  predicts (PlayerMove)
  follows keep_paddle_under_ball, input(GameState)
  feeds output
end

curriculum ball_location_curriculum
  train ball_location
  with simulator breakout_simulator
  objective ball_location_distance

    lesson constant_breakout
      configure           # configure to constant values
        constrain bricks_percent with Float32{0.5},
        constrain level with Int32{1},    # e.g. level = 1
        constrain paddle_width with Int8{4}
      train
        from frame in breakout_simulator
        select frame
        send frame
      test
        from frame in breakout_simulator
        select frame
        send frame
      until
        minimize ball_location_distance

    lesson vary_breakout follows constant_breakout
      configure          # configure to type constraints
      constrain bricks_percent with Float32{0.1:0.01:1.0},
      constrain level with Int32{1:100}, # e.g. level varies from 1..100
      constrain paddle_width with Int8{1:4}
    train
      from frame in breakout_simulator
      select frame
      send frame
    test
      from frame in breakout_simulator
      select frame
      send frame
    until
      minimize ball_location_distance
end

In this example we show lessons that break into stages the task of playing the game breakout.

  • The first lesson, constant_breakout, trains the machine with a set of fixed values as configuration parameters.
  • The second lesson, vary_breakout, which follows constant_breakout, trains the machine with a set of configuration parameters that vary according to specified type constraints.

The two lessons in our example, constant_breakout and vary_breakout, are different in their configure clause. The first sets the fields in the configuration schema to constant values and the second lesson, vary_breakout, generates sets of values constrained by the type constraint.

Note that the identifiers specified after the constrain keyword in our example specify fields in the configuration schema for the simulator. These fields are bricks_percent, level, and paddle_width. When such fields are initialized with values from a type constraint they are called placeholders. This means that the name is is not the name of a specific value but rather it is the name of a range of values which will be input during training.

Schema declarations can also use type constraints. They are discussed in depth here.

Lesson Subclauses

In this section we discuss the follows clause, the configure clause, the test and train clause, and the until clause.

  • Lessons can be ordered, using the follows clause. This will specify the order of lessons in training and can be more efficient.

  • Lessons are configured, using the configure clause. This configures data for the lesson.

  • The test and train clauses describe testing and training, and are optional. Note that whereas the test clause is optional for any particular lesson, if the last lesson has no test clause it is an error.

  • The until clause describes success for the objective function.

Follows Clause

Follows Clause Syntax

lesson <lessonName>
   followsClause?

followsClause ::=
follows 
    lessonName [ ',' lessonName]* 

The follows clause can be used to order lessons. Note that this ordering is a suggestion to the instructor, not a hard and fast rule. If there is no follows clause and the lessons are executed in parallel, training will be slower.

Usage

The follows clause is optional.

Configure Clause

Configure Clause Syntax

configureClause ::=
configure
    [ constrainClause [ ',' constrainClause]* ]*

constrainClause := 
constrain <schemaFieldName> with  

    ( constrainedType )

The configure clause function is to configure data for training and testing. The configure clause has constrain subclauses that each specify a placeholder for a field in the configuration schema. The placeholder specifies a range of values for the field through constrained types and range expressions.

Usage

A configure clause is required.

Placeholder

The constraints in the configure clause configure the fields in the configuration schema for the simulator. These fields are called placeholders. In the accompanying example level is a placeholder. It is also a field in the configuration schema.

constrain level with Int32{1:10}   # level is a placeholder name. 

A placeholder is not the name of a specific value but rather it is the name associated with a set of possible values which will be input by the instructor during training. The characteristics of this set are specified in the constraint. In this case, the values will be integers between 1 and 10. Note there is no assumption of order. The instructor will randomly choose members from this set.

The type Int32 {1:10} is called a constrained type. The syntax {1:10} is called a range expression. These topics are discussed in depth in the Schema chapter.

The field definition of level in schema BreakoutConfig is Int32 level. In order to be a valid constraint, the placeholder definition for level must conform to the field definition. This means the types must be identical. Also the range expression on the placeholder must specify a subset of the values on the field definition. If there is no range expression on the field definition, the maximum range of values for the type is assumed, so any valid range expression on the placeholder would be valid.

Example

schema BreakoutConfig 
  Int32  level,                     # 'level', 'paddle_width', 'bricks_percent' 
  Int8   paddle_width,              # are matched below in constrain clauses 
  Float32 bricks_percent 
end

curriculum keep_paddle_under_ball_curriculum 
  train keep_paddle_under_ball 
  with simulator breakout_sim(BreakoutConfig) 
  objective ball_paddle_distance 

    lesson track_ball 
      configure breakout_sim 
        constrain paddle_width  with Int8{1:10},
        constrain level with Int32{1:10}, 
        constrain bricks_percent with Float32{0.1:0.01:1.0}  
      until 
        maximize ball_paddle_distance 
end

The accompanying example gives an overview of configuration using these Inkling code fragments in the context of a curriculum. The simulator in the curriculum uses the BreakoutConfig schema. Note how the field names and types in the simulator schema match up with the names and types of the placeholders in the constrain clauses.

In this example we show how configuration works for bricks_percent.

The instructor selects values for bricks_percent from the given range. Float32 {0.1:0.01:1.0} is an Inkling specification for a constrained type. In a constrained type the values are all Float32 but they also obey the constraint specified.

We can also configure bricks_percent to take a constant value:

constrain bricks_percent with Float32 {1.0}

Float32 {1.0} is a Float32 constrained to take the value 1.0. This is the value list form of the range expression. You can add values to the list as long as they are of the same type, so the following is also valid:

constrain bricks_percent with Float32 {1.0, 1.5}

Train and Test Clause

Train and Test Clause Syntax

trainClause ::=
train
  from <item_name> in <simulator_name>
  select <item_name>
  send <item_name>

testClause ::=
test
  from <item_name> in <simulator_name>
  select <item_name>
  send <item_name>

The test and train clauses describe testing and training.

The from subclause in the test/train syntax is used to name, describe, and select the training data that is sent by the simulator to the lesson.

The test clause and the train clause have identical syntax except for their keyword (train or test).

Usage

The test and train clauses are optional.

If neither the test or train lesson clauses are present, defaults for both clauses are generated. The default in both cases is:

from item in <simulator_name> select item send item

(Here <simulator_name> refers to the name of the simulator being used by the lesson.)

If one of the train and test clauses is present, no defaults are generated.

If the train clause is not present, the return schema of the simulator must exactly match the input schema to the network.

The test clause is not required for any particular lesson. But if the final lesson does not have a test clause that is an error.

Example

In this example we show train and test clauses.

curriculum high_score_curriculum
  train high_score
  with simulator breakout_simulator
  objective score
    lesson score_lesson
      configure
        constrain bricks_percent with Float32{1.0},
        constrain level with Int32{1:100},
        constrain paddle_width with Int8{1:4}
      train
        from frame in breakout_simulator
        select frame
        send frame
      test
        from frame in breakout_simulator
        select frame
        send frame
      until
        maximize score
end

What is sent (via send) to the neural network as a result of the train must have the same schema as the system’s input schema. In this case that is GameState. Note that GameState is declared as the output schema of the simulator.

The data that comes out of the lesson will always flow into the input keyword. The system is calculating a subgraph between the input and the concept being trained and that portion of the mental model is involved in the training.

The curriculum represents a collection of lessons which collectively train a subgraph of the mental model. The lesson represents a phase in training a subgraph of the mental model. The lessons represent the phases of training.

All subgraphs begin with input and end with the concept under training and contain all nodes in between.

For training with a simulator, the trained statement equals the simulator output.

The trained statement can be understood as the input data to the trained network (or mental model).

The train clause is optional. If it is not present a default is generated that will send the simulator output (conforming to the simulator output schema) to the neural network.

Until Clause

Until Clause Syntax

The until clause in the lesson specifies the termination condition for training.

untilClause ::=
until
      [ minimize | maximize ] <objectiveFunctionName>
    |
      <objectiveFunctionName> relOp constantExpression

relOp ::=
  '==' | '<' | '>' | '<=' | '>='

Usage

The until clause is required.

Example

There are several examples of the until clause above. They are excerpted in the code panel.

      until 
        maximize ball_paddle_distance 
      until
        maximize score

These both specify that training should continue until the return value of the objective function is maximized.

Schemas

This is the reference for the keyword schema. Also covered are the definitions and uses of Inkling types, including type constraints. These are used in schema declarations.

Schema Declarations

   schema bar       # schema declaration
      Bool field1,
      Int8 field2
   end

Schemas are declared with the schema statement.

In Inkling a schema describes a named record and its contained fields. Each field in a schema has a name and a type. A field may also have a type constraint that constrains the values that the datum described by this field will take.

   concept foo
      is classifier
      predicts (bar) # schema reference
   end

Statements (such as the concept statement) can reference schemas by name.

   concept foo
      is classifier
      predicts (Bool field1, Int8 field2) # anonymous schema
   end

Schemas can also be anonymous. In that case, instead of a schema name, a list of named fields is present.

Schemas describe the structure of data in Inkling streams, such as the predefined input and output streams. In addition, many Inkling statements (for example concept and curriculum) use schema references to describe the data that flows in and out of the construct.

Schema Declaration Syntax

schemaStmt := 
schema <name>                      
     fieldDclnList 
end

fieldDclnList   :=  fieldDcln [',' fieldDcln  ]*  

fieldDcln       :=  scalarDcln   | structureDcln 

scalarDcln      :=  primitiveType   rangeExpression? <name>

structureDcln   :=  structure_type   structure_init <name> 

structure_type  :=  Luminance | Matrix | Vector

structure_init  :=  '(' 
                        luminance_init | matrix_init | vector_init
                    ')'

luminance_init  :=  integerLiteral  ',' integerLiteral 

matrix_init     :=  '(' primitiveType [ ',' primitivetype ]* ')' 
                        ',' integerLiteral [ ',' integerLiteral]* 

vector_init     :=  primitiveType   rangeExpression? ','  integerLiteral 

In the Schema Declaration Syntax, you will see references to Inkling primitive types and structured types (Luminance, Matrix). These are discussed in in the Structured Types section.

Usage

Schema fields must be separated from each other by commas. Schema declarations are terminated by the end keyword.

Field types can be any of the Inkling primitive types and Inkling structured types.

In matrix initialization, a parenthesized list of types is followed by a list of dimensions. The number of types and the number of dimensions must match.

Matrix size must be an integral constant.

A schema field that has a primitive type can also have a type constraint that constrains the set of potential values for that field. Examples and syntax of type constraints are included in the Constrained Types section.

Discussion

Schemas tell the BRAIN system how to translate large matrices (tensors) to usable values.

Note the inkling compiler does not do this translation, that is a runtime transformation. However the inkling compiler performs static checks to verify that the schemas are valid in the context in which they are used.

  schema MNIST_schema
    Luminance(28, 28) image # structured type
  end

  schema my_schema
    Int32 x,                # primitive type
    Int32{1:5} z            # primitive type with type constraint
  end

The code panel contains a few example schemas. The last field z in my_schema has constrained type Int32{1:5}. For more information on this feature see Constrained Types.

Schema References

   concept MyConcept
      is classifier
      predicts (MySchema)       # a schema reference
   end

Inkling statements can reference schemas by name.

Anonymous Schema
   concept MyConcept
      is classifier
      predicts (UInt8 MyField)  # a anonymous schema 
   end

Anywhere a schema name can be referenced, a list of fields can appear. This is an anonymous schema. Anonymous schema can also be empty (that is, they can contain no field definitions), if allowed in context.

Schema Reference Syntax

schemaRef :=                 
     '('   ')'                  # empty anonymous schema
 |   '(' <name> ')'             # named schema
 |   '('  <fieldDclnList> ')    # non-empty anonymous schema

Types

Inkling supports both primitive types and structured types.

Primitive Types

Primitive Types List

primitiveType ::=
  Double | Float64 | Float32 | Int8 | Int16 | Int32 |
  Int64 | UInt8 | UInt16 | UInt32  | UInt64 | Bool | String

The Inkling set of primitive types includes numeric, string, and boolean types.

In the code panel you will see the set of primitive types which are used in schema declarations.

The integer suffix indicates the size in bits of the type.

Integer types beginning with ‘U’ are unsigned.

Double and Float64 are synonyms.

Structured Types

Structured Types List

structure_type ::=                      # syntax
  Luminance 
  | Matrix
schema BallLocationSchema               # example
  Luminance(84, 336) pixels,    
  Matrix(UInt32, 1, 2) location 
end

Structured types in Inkling are intended to support common Machine Learning types. The only Machine Learning type currently supported is Luminance. This support will be expanded to Matrix and Vector, among others in the future.

Constrained Types and Range Expressions
  schema my_schema
    Int32{1:5} z            # primitive type with type constraint
  end

A constrained type is a type which is associated with a constraint. The type plus constraint is effectively a set definition. A field declared with a constrained type can only take values that are a member of the set defined by the constraint. In the example in the code panel, the field z can only take on values between 1 and 5.

Constrained types are supported in schemas and in lesson clauses. Types are constrained by means of range expressions. A range expression on a type has the effect of constraining the values of the type to values defined by the range expression.

  • In a schema the range expression constrains the values in the field.
  • In lessons the range expression constrains the values of the placeholder being configured.
schema MyOutput 
   UInt8 {0,1,2,3,4}    label,       # a list of UInt8 values
   Int64 {0:5:100}      x,           # start:step:stop, step= 5, 0..100
   Int64 {0:100}        y,           # start:stop, step= 1, 0..100
   Int64 {0..100:25}    z,           # start:stop, numsteps=25, step= 4, 0..100
   Float32 {0..2:5}     a            # gives (0, .5., 1.0, 1.5, 2.0)
end

The syntax for a constrained type is the same for schema fields and placeholder expressions.

In the code panel are some examples of type constraints as they could appear in a schema definition. Curly braces delineate the range expression.

Inkling supports numeric range expressions and value list range expressions.

Numeric Range Expressions

Numeric Range Expression Syntax

 Double | Float64 | Float32 | Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64
  '{' 
      start ':' [ step':']? stop       # 'colon range'
      |  
      start '.' '.' stop ':' numSteps  # 'dot range'
  '}' 

For numeric range expressions there are two types:

  • colon range
  • dot range

A dot range must specify numSteps. numSteps must be a positive integer constant. Today, dot range expressions cannot be used in predicts schemas with is estimator concepts.

A colon range can specifies an optional step size. If step size is omitted, the range includes all values of the given type between start and end. For example, in colon range expressions with integer types, if an explicit step is not specified, the default step size is 1.

Step size is required for FloatXX fields in the output schemas for is classifier concepts.

Note that today, all fields in schemas used in the predicts clause of is estimator concepts must have type FloatXX with no explicit step.

Usage

For some ranges, it is clearer to specify the number of steps in the range then the step size. For example Int32{-11003..743299:100} makes it clear that a large range has 100 elements. Using step size for such a range would not clearly communicate that information.

In other contexts, the step size is of interest, and we use the colon range. For example, in the range Int32{0:2:10}, we have divided the range into elements (0,2,4,6,8,10). The step size of two communicates information about the distribution of values in the range.

Int64  { 0:4:1 }     # is invalid. The step size is larger than the range.
Int64  { 0..1:4 }    # is invalid. Values generated are floating point not integer. 
Float32{-1..1:10 }   # is valid. Negative bounds allowed.
Int8   {0:-4:-100}   # is valid. stop < start is valid if and only if step is negative. 
UInt32 {-10:10}      # is invalid. Unsigned integer range contains signed values.

The code panel contains some examples of valid and invalid ranges.

For colon range, step can be a floating point number.

For dot range, number of steps (numSteps) must be a positive integer.

For colon range, the step size can be negative only if stop is less than start.

For both colon range and dot range:

  • The start point is inclusive (it is included in the values of the range) and fixed.

  • The start point is exact (to the maximum extent possible if the range expression type is floating point).

  • The end point must be reachable from the range start by applying the step. (The range must be bounded.)

  • The end point may or may not be included in the values of the range. If you land on it exactly it is in the range. If you don’t land on it exactly it is not in the range. For example Int8 {0:3:10} gives you (0, 3, 6, 9). The end point 10 is not included. The specification of 10 as the stop point is not an error (because it is a limit, not an endpoint).

You can think of the end point as a limit. It is included only if, after applying the (step or numSteps) expression, you land on it exactly. Otherwise the highest value landed on which is less than the end point is the final value in the range.

Value List Range Expressions

 

Value List Range Expression Syntax

primitiveType 
    '{' 
        [ integerLiteral [ ',' integerLiteral ]* ]
     |  [ floatLiteral [ ',' floatLiteral ]* ]
     |  [ booleanLiteral [ ',' booleanLiteral ]? ]
    '}' 

Inkling supports range expressions for value lists.

A value list range expression can be defined for numeric types and the primitive type Bool.

Usage

    UInt8  {7, 7, 7, 7}             # is valid.
    UInt8  {7, -7, 7, 7}            # is invalid (negative integer in unsigned range).

Value list range expressions support allowing arbitrary lists of values to constitute the range expression set, rather than start and end points.

A value list range expression must have all its elements be valid values for the specified type.

A value list range expression must conform to the signed or unsigned attribute of the specified type (if there is such an attribute).

Schema Matching

Inkling uses schemas to understand and interpret the data format of streams. The Inkling compiler performs schema match checking and will report errors if schemas which are expected to match do not.

schema GameState
  Luminance(84, 336) pixels
end
 
concept keep_paddle_under_ball 
  is classifier
  predicts (PlayerMove)
  follows ball_location, 
          input(GameState)           # <--- (1) MATCH 
end
 
concept high_score 
  is classifier
  predicts (PlayerMove)
  follows keep_paddle_under_ball, 
          input(GameState)           # <--- (1) MATCH 
  feeds output
end
 
concept ball_location 
  is estimator
  predicts (Matrix(UInt32, 1, 2) location)
  follows 
    input(Luminance(84, 336) pixels) # <--- (2) MATCH
end
 
concept ball_X_location is estimator
  predicts (Uint32 X_location)
  follows 
    input(Luminance(84, 330) pixels) # <--- (3) NO MATCH
end

Matching for schemas is both structural and name based. Field names matter. Types matter.

  • Two references by name to the same schema match because a schema matches itself.
  • Two references to different schema names match if both schemas define the same list of field types in the same order with the same names. For structured types whose declaration includes size, the sizes must be equal.
  • Two anonymous schemas match if both define the same field types in the same order with the same names.
  • A schema referenced by name matches an anonymous schema if both define the same field types in the same order with the same names.

The associated example shows successful schema matching and failed schema matching. The rule being checked is that all uses of input use the same schema.

  • (1) shows a valid schema match by name (for input).
  • (2) shows a valid match by field type, field name, and size (for input).
  • (3) shows a failed match (size is not equal, for input).

Every Inkling program has the predefined stream input available to it. Since there is a single stream associated with the keyword input, there can be only one definition of the data format of that stream. Once defined, the data format of a stream cannot change dynamically. If that schema changes inkling compiler will flag that change as an error. Thus any reference to the input stream must have a schema that matches all other schemas used with the input stream.

Constraint Compatibility in the Lesson Configure Clause
simulator breakout_simulator(BreakoutConfig)
   state (GameState)
   action(PlayerMove)
End
 
schema BreakoutConfig
  UInt32 level,
  UInt8{1:4} paddle_width,
  Float32 bricks_percent
end
 
curriculum ball_location_curriculum
  train ball_location
  with simulator breakout_simulator
  objective ball_location_distance

    lesson more_bricks follows no_bricks
      configure
        constrain bricks_percent with Float32{0.1:0.01:1.0},
        constrain level with UInt32{1:100},
        constrain paddle_width with UInt8{1:4}
      train
        from frame in breakout_simulator
        select frame
        send frame
      test
        from frame in breakout_simulator
        select frame
        send frame
      until
        minimize ball_location_distance
end

Constraints can be specified in the lesson configure clause. These constraints must be compatible with the configuration schema of the associated simulator. The code panel contains some Inkling code from the breakout program to show an example.

The constraints in a lesson configure clause are constraints on the fields of the configuration schema of the simulator. Here, the lesson configure constraints are constraining the fields of schema BreakoutConfig.

Given simulator breakout_simulator, the constraints in the lesson configure section must have these characteristics to be compatible with schema BreakoutConfig:

  1. They must specify exactly the same type and field name as the schema declaration. A constraint cannot use an Int8 where a UInt32 was originally specified.
  2. For the range expression, the range in the configure must specify exactly the same range or a subrange as the schema declaration.
  3. Step size or numSteps should not be specified in the configuration schema of the simulator. They can be specified in the lesson configure clause. Specifically, in the BreakoutConfig schema above, UInt8{1:4} paddle_width is valid but UInt8{1:2:4} paddle_width would not be valid.

Here is the constraint for level that is specified in the lesson more_bricks configure clause:

constrain level with UInt32{1:100}

Here is the constraint for level in the simulator configuration schema BreakoutConfig:

UInt32 level

Examples of valid constraints:

constrain paddle_width with UInt8{1:3}    # ok: a subset of constraint set

constrain paddle_width with UInt8{2}      # ok: value in constraint set

Examples of invalid constraints

constrain paddle_width with UInt8{1:5}    # not a subset of constraint set

constrain paddle_width with UInt8{7}      # value not in constraint set

The lesson configure clause constraint is compatible with the simulator configuration constraint because the latter declared no range at all and any range expression is a subrange of all the values available to a type.

The field paddle_width in the simulator configuration schema BreakoutConfig does specify a range expression:

UInt8{1:4} paddle_width

The corresponding range expression in the lesson configure clause is identical:

constrain paddle_width with UInt8{1:4}

These are compatible.

In the examples in the code panel, we show more examples of valid and invalid lesson configure constraints for the field paddle_width.

Simulators

simulator MySimulator(MySchemaConfig)
  state (MyStateSchema)
  action (MyActionSchema)
end

Curriculum statements reference an associated simulator in the with clause. Each simulator used in a curriculum must be declared in a simulator statement. 

The simulator statement describes the interface to a simulator. Simulators are generally implemented in python or C++. The Inkling program does not contain code for the simulator itself. Instead the Inkling program defines how the simulator is used to train Inkling concepts.

Simulator Clause Syntax

simulator <simulatorName>'('<configurationSchema>')' 
  state '('<stateSchema>')'     # simulator state
  control '('<controlSchema>')' # training concept predicts schema
end

A simulator statement associates a set of schemas with the simulator.

  • The configuration schema is used for initialization.
  • The action schema describes the simulator input.
  • The state schema describes the simulator output.

Usage

curriculum ball_location_curriculum
  train ball_location
  with simulator breakout_simulator
  objective ball_location_distance
  ... 

The curriculum specifies which simulator it uses. The example in the code panel shows the use of simulator breakout_simulator.

Discussion

Simulators are virtual environments designed to simulate a real world situation or problem. Every simulator has a state, a representation of the world inside the virtual environment. This state changes over time in response to actions taken by an agent.

The simulator and the BRAIN are in a loop where a BRAIN is receiving a frame of state from the simulator, followed by the BRAIN selecting a next action. The simulator then changes state in response to that action. The BRAIN then receives this new updated frame of state from the simulator and selects a new next action, so on and so on, until the BRAIN learns how to best operate the simulation.

Our Library Reference describes the classes and methods used to connect an existing simulator or create a new simulator in Python. Find the Center is an example of a basic simulator implementation.

Example


simulator breakout_simulator(BreakoutConfig)
  action  (PlayerMove)
  state (GameState)
end

schema GameState
  Luminance(84, 336) pixels
end

schema PlayerMove
  Int8{-1, 0, 1} move
end

schema BreakoutConfig
  UInt32 level,
  UInt8{1:4} paddle_width,
  Float32 bricks_percent
end

concept high_score is classifier
  predicts (PlayerMove)
  follows input(GameState)
  feeds output
end

curriculum high_score_curriculum
  train high_score
  with simulator breakout_simulator
  objective score
    lesson score_lesson
      configure
        constrain bricks_percent with Float32{1.0},
        constrain level with UInt32{1:100},
        constrain paddle_width with UInt8{1:4}
      train
        from frame in breakout_simulator
        select frame
        send frame
      test
        from frame in breakout_simulator
        select frame
        send frame
      until
        maximize score
end

In this example we show some of the Inkling code for training the game Breakout. The curriculum with clause specifies breakout_simulator, and the simulator statement specifies the action, state, and configuration schemas.

The curriculum specifies the objective function, in this case it is score. The objective function (which is sometimes called the reward function in the literature) is implemented in the simulator. (Eventually implementing objective functions in Inkling will be supported.)