NAV Navbar

Inkling Overview

# Inkling Code Snippets will be shown here  #
# Click the "Syntax" tab for Inkling Syntax #
# Inkling Syntax will be shown here                 #
# Click the "Inkling" tab for Inkling Code Snippets #

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 pedalogical programming.

An Inkling file contains the concepts (what you want to teach the AI), and curriculum (how you want to teach the concepts) necessary to train your BRAIN.

Lexical Structure

Reference for the lexical structure of the Inkling language.

What is it?

The lexical structure of Inkling includes these lexical elements:

  • comment: specifies comment format.
  • 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.


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


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 false feeds Float32
Float64 follows format from generator
hard import in input Int16
Int32 Int64 Int8 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


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


Inkling supports numeric literals (floating point and integer) as well as string literals.

  • String Literals

String literals are enclosed in double quotes.

  • Integer Literals

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

  • Floating Point Literals

Floating point literals can be Float32 or Float64 (double). Select the Inkling tab to see some floating point literals:

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


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

Operators Table

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


Reference for the keyword concept. Also, describes the keywords: predicts, input, output, is, follows, end, and feeds.

What is it?

  • concept: (the keyword) declares an abstract concept for the system to learn.
  • is: specifies the kind of prediction the trained concept will produce (classifier or estimator).
  • predicts: declares the concept’s output.
  • follows: declares the concepts or streams the concept gets input from.
  • feeds: declares the list of concepts and streams that have this concept’s output as input.
  • end: delimiter that declares the end of this statement.

Why do I use it?

A concept statement describes what the computer will learn. It can be a feature (such as a curvy line in an image) or a goal (such as high score in a game).

How Do I Use It?

concept conceptName
  is classifier                 # or 'is estimator'
  predicts (outputSchema)
  follows preceedingConcept1, input(schemaName)
  feeds output, subsequentconcept

If you selet the Inkling tab a typical concept statement will be shown. Its structure and keywords are explained in the following sections.

Concept Rules

  • The concept must be named after the concept keyword.
  • The is keyword specifies the kind of prediction the trained concept will produce. For example, a concept can specify is classifier. This means that the trained concept will categorize its input. Email, for example, can be classified as spam or not spam. Another option with this keyword is estimator.
  • The concept must declare an output schema after predicts. The output schema describes the data produced by the trained concept. For example if this concept classifies email into spam and not spam, the output schema for the concept would be a Bool. The output schema can be a named schema, where the name refers to a full schema definition elsewhere, or it can be anonymous, which is a parenthesized list of name, type pairs. See the section on schema declarations for more information.
  • A trained concept gets input from streams or (if multiple concepts are used) from another concept. input (the keyword) refers to the stream that is the original input to the system. All data flowing through the system has a schema associated with it. In some cases this is calculated rather than explicit.
  • If the input keyword appears in the follows list, it means that the input stream flowing into this concept comes from outside the BRAIN. The input keyword must always be accompanied by a schema (named or anonymous) because the data stream originates outside the BRAIN; if no schema was present, data types and formats being input would be unknown.
  • The feeds list is a list of concepts and streams (including the predefined output stream) for which this concept’s output is a source.
  • The input keyword cannot not appear in the feeds list and the output keyword cannot appear in the follows list.
  • The concept statement is terminated by the end keyword.

Concept Statement Syntax

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


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

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

Select the Syntax tab to show syntax for the concept statement and its input sources and output targets.

Concept Examples

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

Select the Inkling tab to display the Inkling source.


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

In this example:

  • conceptName: get_high_score
  • kind: classifier
  • predicts: PlayerMove
  • input(schemaName): input(GameState)
  • dependent: output


concept Digit
  is classifier
  predicts MNIST_output
  follows Curvature, Segments, input(MNIST_input)

In this example:

  • conceptName: Digit
  • kind: classifier
  • predicts: MNIST_output
  • Curvature: a concept
  • Segments: another concept
  • input(schemaName): input(MNIST_input)
  • feeds: output


concept Curvature
  is classifier
  predicts (curve_output)
  follows input(MNIST_input)

In this example:

  • conceptName: Curvature
  • kind: classifier
  • predicts: curve_output
  • input(schemaName): input(MNIST_input)


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


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.

What is it?

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.

Why do I use it?

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.

How do I use it?

schema MySchema                   # declare
   UInt8  field1,
   UInt32 field2

concept MyConcept
  is classifier
    predicts (MySchema)           # use
    follows input(UInt64 i)       # anonymous
    feeds output

Select the Inkling tab to show a sample schema declaration and an example of its use. Note that a schema reference can be anonymous. That means a list of name, type pairs can appear where a schema name could appear. ‍

Schema Rules

  • Inkling statements can reference schemas by name. Above, MyConcept uses MySchema as its predicts schema.
  • Statements can use anonymous schemas. That means that a list of fields appears where a schema name could appear. Above, after follows, the predefined stream input has an anonymous schema with one field. This is useful in cases where you will only need that information once. In general, anywhere a schema name can appear, an anonymous schema can appear.
  • The set of types supported with schema fields consists of the set of Inkling primitive types and the set of Inkling structured types. These sets are specified in the Inkling Types section.
  • 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 this chapter.

Schema Declaration Syntax

schemaStatement ::=
  schema <schemaName>

fieldDeclarationList ::=
   [',' fieldDeclaration  ]*

fieldDeclaration  ::=  

scalarDeclaration  ::=   
  concreteType rangeExpression?  
    <name> [ '[' arraySizeLiteral ']' ]* 

structureDeclaration ::= 
  structure_type structure_init 
    <name> [ '[' arraySizeLiteral ']' ]*

structure_type ::= 
  Luminance | Matrix


luminance_init ::=  
  integerLiteral ',' integerLiteral 

matrix_init ::= 
  '(' concreteType 
      [ ',' concretetype ]* 

scalarDeclaration ::=
  primitiveType typeConstraint?
    <name> [ '[' arraySizeLiteral ']' ]*

Select the Syntax tab to view the schema declaration syntax.

In the syntax you will see references to Inkling primitive types and structure types (Luminance, Matrix). These are discussed in in the Inkling Types section.

Schema Reference Syntax

schemaReference ::=
  '(' <name> ')'                  # named reference
  '(' <fieldDeclarationList> ')'  # anonymous reference

Select the Syntax tab to view the schema reference syntax.

A named schema is referenced by its name. An anonymous schema is referenced by its list of fields.

Schema Example

schema MNIST_training_data_schema
  UInt8 label,
  Luminance(28, 28) image

Select the Inkling tab to show a schema that has a field with a primitive type and a field with a structured type.

Inkling Types

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

Select the Syntax tab to show the Inkling 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.

Structured Types
structure_type ::= 
  Luminance | Matrix

Inkling currently supports the types Matrix and Luminance. (This list will be expanded.)

See the schema declaration syntax for the complete syntax of structure declaration.

Constrained Types
  # Example: Range expression

  schema Schema1
    Int8 {0:3:10} field   # start:step:stop (all are numeric literals)

 # Curly braces delineate the range expression.
 # The values in this range are: (0, 3, 6, 9).
 # Note that the specification of 10 as the stop does not mean
 # 10 must be in the range (because it is a limit, not 
 # necessarily an endpoint).

Constrained types are supported in schemas and also in lessons. They are constrained by means of a special type of expression called a range expression.

A range expression has the effect of constraining the values of the type to values defined by the range expression. In a schema this constrains the values in the field. In lessons this constrains the values of the placeholder being configured. In both cases the syntax is the same.

Select the Inkling tab to see an example of a constrained type in a schema definition.

Constrained Type Syntax

constrainedType ::=
  start ':' [ step':']? stop      # A 'colon range'. Specifies 'step' 
  start '.' '.' stop ':' numSteps # A 'dot range'. Specifies 'numsteps'.

Select the Syntax tab to view the constrained type syntax.

Note that start, stop, step, numSteps are all numeric literals.

Range Expression Rules

There are three forms of range expressions which Inkling supports. Select the Inkling tab to see an example of each type.

# Example: Value list range expression
schema Schema2
  UInt8  {0,1,2,3,4}   num, # a set of UInt8 values
  String {"a", "bc"}   cat  # a set of Strings
  • Value list range expression

A value list range expression is simply a list of values. These range expressions specify sets of values in which each value is explicitly listed.

# Example: Range expression, colon range type
schema Schema3
  Int64  { 0:5:100 }   x,   # start:step:stop, step= 5,0..100
  Int64  { 0:100 }     y    # start:stop, step= 1, 0..100
  • Colon range expression

Colon range expressions specify values for the start, the step, and the stop.

  • If the step value is missing, 1 is the default.
  • Step can be a floating point number.
  • The step size can be negative only if stop < start.
# Example: Range expression, dot range type
schema Schema4
  Int64  { 0..100:25 } z,   # start:stop, numsteps=25
  Float32 { 0..2:5}    a    # yields (0, 0.5, 1.0, 1.5, 2.0)
  • Dot range expression

Dot range expressions specify values for start, stop, and number of steps.

  • The number of steps (numSteps) must be a positive integer.

  • Numeric Range Expression Start Point

The start point of a numeric range is inclusive (it is included in the values of the range) and fixed. The range start point is exact (to the maximum extent possible if the range expression type is floating point).

  • Numeric Range Expression End Point
 # Valid and Invalid Range Expressions.

 Int64  { 0:4:1 }  field1,  # INVALID. step size > range.
 Int64  { 0..1:4 } field2,  # INVALID. Values are floating point not integer.
 Float32{-1..1:10 } field3, # VALID.   Negative bounds allowed.
 Int8 {0:-4:-100} field4,   # VALID.   stop  < start is valid if step is negative.
 UInt32 {-10:10}  field4    # INVALID. Unsigned integer range contains signed values.

The end point specified in the range expression may or may not be included in the values of the range. The range end is a limit. That means that if applying the step results in landing exactly on the end point, then the end point is part of the range. Otherwise the highest value landed on that is less than the end point is the final value in the range.

Select the Inkling tab to view some examples of valid and invalid ranges.

Training Source

The Bonsai Platform supports training with both real and synthetic data. The data, simulator and generator keywords are used to describe what kind of training source you would like to use for training.


Simulators are interactive virtual environments. Every simulator has state, a representation of the world inside the virtual environment. This state almost always changes over time and in response to actions taken by an AI or other agent. The AI and the simulator are in a loop where a BRAIN is receiving a frame of state from the simulator, then, the BRAIN is selecting a next action. Next, the simulator 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.


Generators produce labeled data programmatically. This data is effectively infinite. A generator could, for example, produce a random (but known) integer, set of line segments, etc.


Data is information related to the scenario being trained comprising of columns of information with input values and expected labels or desired predicted values. Data is used both for training and evaluating the quality of training. Examples of training data include a collection of images and labels or the rows and columns of a spreadsheet.

Simulator Clause Syntax

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

Select the Syntax tab to see the Simulator clause syntax.

When a simulator is used for training, use the simulator clause. It is required to pass a into the simulator, even if the configuration is empty.

The Mountain Car example of a simulator shows the implementation of this clause in a curriculum.


Reference for the keyword curriculum. Also, describes the keywords: train, with

What is it?

The curriculum (keyword) declares a set of lessons that are used to teach concepts. Each curriculum contains a lesson or set of lessons and trains a single concept.

Why do I use it?

A curriculum is used to teach a concept. The curriculum defines what concept is being taught. Every concept needs a corresponding curriculum to teach it. A curriculum defines a set of lessons used to train the concept.

How do I use it?

curriculum curriculumName
  train conceptName
  with trainingSpecifier  # one of data, simulator, or generator
  objective objectiveName
    # lessons are specified here.

Select the Inkling tab to see a simple form of a curriculum statement.

The trainingSpecifier specifies either data, simulator, or generator as the training source. Refer back to Training Source for more information on what the differences are.

The objective specifies the termination condition for training.

Mountain Car Example

simulator mountaincar_simulator(MountainCarConfig) 
  state (GameState)
  control (Action)
curriculum high_score_curriculum
train high_score
with simulator mountaincar_simulator 
objective open_ai_gym_default_objective
  lesson get_high_score
      constrain episode_length with Int8{-1},
      constrain deque_size with UInt8{1}
      maximize open_ai_gym_default_objective

Select the Inkling tab to view an excerpt of the code in the game Mountain Car from OpenAI Gym as written in Inkling. This illustrates the simulator clause. (To explore this example more fully, refer to it in our Examples chapter.)

The simulator clause declares the simulator name and two schemas. The first 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 MountainCarConfig. In the example, the configure clause of lesson get_high_score initializes this configuration.

# Configuration schema declaration
schema MountainCarConfig
  Int8 episode_length,
  UInt8 deque_size

The names in the configuration schema are the names referenced in the configure clause of lesson get_high_score. When the 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 mountaincar this schema is called GameState and prior state consists of prior position.

# State schema definition
schema GameState
  Float32 x_position,
  Float32 x_velocity

In order to determine what our next move will be, the training will use the previous position as input.

Finally, note that high_score_curriculum trains a concept called high_score. (It’s quite clear what we are aiming for with this curriculum!)

# Predict schema Action (see concept high_score)
schema Action
  Int8{0, 1, 2} action # these values describe game moves

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

The concept high_score trains the Brain to select the next move, which will have one of the values specified in the Action schema range expression.

Note that the predefined stream input has the schema GameState. This reflects that fact that the simulator has state. The previous move is the state which is input into the selection of the next move.

The predicts schema Action also appears in the simulator clause discussed above. It is after the keyword control. In general the control schema is the predicts schema of the concept being trained.

So far we have presented a simple version of the curriculum. Inkling supports multiple simulators and generators within a single curriculum. Here is the full syntax for the curriculum statement, which introduces a using clause and a with clause (where using and with will specify simulators). These were not needed in our example above because we were using a single simulator.

Curriculum Rules

  • One curriculum per concept.
  • Every concept must have a curriculum.
  • Every simulator must be declared with a simulator clause.
  • Lessons and tests can occur in any order.
  • If the using clause is present (that is, if the simplified curriculum syntax is not being used), there must be one using clause for every with clause.
  • The objective is always required.

Curriculum Statement Syntax

curriculumStatement ::=
curriculum <name>
    train <conceptName>
  withClause                        # with clause

  using <simulatorName>             # using clause
    lessonClause # lesson set for this simulator

  end # using
end # curriculum
withClause ::=
with simulator
  objective <objectiveFunctionName>

Select the Syntax tab to see the Curriculum syntax.

Any simulator referenced in a curriculum must have an associated simulator clause, outlined in Training Source.


Reference for the keyword lesson. Also, describes the keywords: follows, configure, constrain, until, minimize, maximize, configure, with, and end.

What is it?

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

Why do I use it?

Lessons give you control over the training of the mental model. They allow you to break down the training of the concept into phases where each phase is implemented by a lesson.

How do I use it?

lesson lessonName
    follows prevLessonName

Select the Syntax tab to show the clauses a lesson can contain.

Lessons allow the machine to learn the concept in stages rather than all at once.

Here are the overall lesson rules:

  • Lesson statements appear within curriculum statements.
  • Lesson statements may contain the following keywords: configure, train, test, and until.
  • Lessons appear after the objective clause in curriculums.
  • Lessons can be ordered, using the follows clause. Note that this ordering is a suggestion to the instructor, not a hard and fast rule.

Lessons have configure, test, train, and until clauses. Some lesson clauses have defaults so if a clause is not specified the default will be in effect. Also in certain circumstances not all clauses are available. See the lesson clauses table in this chapter for the rules which apply to a specific clause.

Breakout Example

schema BreakoutConfig   # configured in lesson configureClause
  UInt32 level,
  UInt8{1:4} paddle_width,
  Float32 bricks_percent

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 UInt32{1},    # e.g. level = 1
        constrain paddle_width with UInt8{4}
        from frame in breakout_simulator
        select frame
        send frame
        from frame in breakout_simulator
        select frame
        send frame
        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 UInt32{1:100}, # e.g. level varies from 1..100
      constrain paddle_width with UInt8{1:4}
      from frame in breakout_simulator
      select frame
      send frame
      from frame in breakout_simulator
      select frame
      send frame
      minimize ball_location_distance

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 constrain name in our example specifies a field 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 often 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.

You can find more discussion of type constraint rules in the schema section. (Schema declarations can also use type constraints.)

Lesson Clause Table

Lesson clauses have defaults so if a clause is not specified the default will be assumed. Also in certain circumstances not all clauses are available. This table specifies the rules. Recall that the trainingSpecifier appears after the keyword with in the curriculum.

Table for Lesson Clauses

training specifier configure clause train clause test clause until clause
data Not allowed. Must have train or test at a minimum. Defaults to none. Must have train or test at a mininum. Defaults to none. Not user specified. Will default to: minimize objectiveName.
generator Required. If neither train nor test is present, defaults to: from item in generator select item send item.image expects item.label. If neither train nor test is present, defaults to: from item in generator select item send item.image expect item.label. If not present, generate default for every lesson. Not user specified. Will default to: minimize objectiveName.
simulator Required. If neither train nor test is present, defaults to: item in simulator select item send item. If neither train nor test is present, defaults to: from item in simulator select item. If not present, generate default for every lesson. Required.
Lesson Clause Rules
  • To summarize the table above, for a lesson associated with a trainingSpecifier of data: one or both of the lesson clauses train and test are required (and there are no default versions of these clauses).
  • Test clause is optional for any particular lesson. However if the last lesson has no test clause it is an error.
  • The follows clause on the lesson is optional. Note: If there is no follows clause and the lessons are executed in parallel, training will be slower.
  • To summarize the table above: for a lesson associated with a trainingSpecifier of generator or simulator:
    • if neither the test or train lesson clauses are present, defaults for both clauses are generated. (See the above table for default details.) Otherwise, no defaults are generated.

Lesson Syntax

lessonStatement ::=
  lesson <lessonName>
    [follows <lessonName>]?

Select the Syntax tab to see the lesson syntax. The syntax for lesson and its subordinate clauses is displayed.

Lesson Configure Clause Syntax
configureClause ::=
  [constrain <configSchemaFieldName> with constrainedType]+

Select the Syntax tab for the syntax for this clause.

Lesson Train/Test Clause Syntax
trainClause ::=
  send <name>
testClause ::=
  send <name>

Select the Syntax tab for the syntax for these clauses.

The test clause and the train clause have identical syntax except for their keyword (train or test). However they both vary depending on the trainingSpecifier in the curriculum.

The from clause in the test/train syntax is used to name and describe the training data that is sent by the system (either from a labeled data set, in the data case, or by the generator or simulator) to the lesson.

Lesson Until Clause Syntax
untilClause ::=
      [ minimize | maximize ] <objectiveFunctionName>
      <objectiveFunctionName> relOp constantExpression

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

Select the Syntax tab for the until clause syntax.

The until clause is required if the curriculum trainingSpecifier is simulator. If it is not present, a default with value minimize will be created.

The until clause in the lesson specifies the termination condition for training. The until clause in our breakout example above was this:

until minimize ball_location_distance

‍ This means train until the curriculum objective (ball_location_distance) is minimized.


Reference for the keyword import. Also, describes the keywords: from

Note: Currently, the only function you can import is split.

What is it?

import (the keyword) describes the usage and location of functions that are part of external libraries.

Why do I use it?

import is used to specify the location and usage external libraries. You specify libraries that you want to use in conjunction with your Inkling code.

How do I use it?

Select the Inkling tab to view two generic import statements.

from libraryName import importName1
import importName2

Import Example

Select the Inkling tab to view the example of an imported function.

from utils import split

  datastore MNIST_data(MNIST_training_data_schema)
  copy mnist-training.csv into MNIST_data with format='csv'

# prepare the data with imported function split

training_data, test_data = split(MNIST_data, 0.8, shuffle=True)