First Steps
We introduce in this section the introductory steps which need to be undertaken in order to exploit the functionality of PyIDF
.
Creating a state transition model
Here we’ll present a simple but complete example of applying imandra region decomposition
to a supervised learning problem with generated input data. We will write a state-tranistion
model for a sequence-to-sequence addition problem. Then we will
use imandra.idf
to create region-based sampling and
First we define two enum types: the Status
enum will describe the overall state our adder and
the Op
enum will encode the next incoming character type:
Next, we define the State
class. The Python State
class definition must be at the core of
each Python model we want to decompose. All the values stored in the state must be declared in
the __init__
constructor of the class:
We’ve declared five state variables:
- two integer operands
op1
andop2
- the overall
status
of the model of ourStatus(Enum)
type - the integer
factor
that will be multiplied by10
with each next incoming character - and the
result
of the addition that we’ll calculate upon receiving theOp.end
action Note that for each variable we’ve provided an initial value and its type via Python’s type hints. Both initial values and types are required if you want to decompose the model.
State transitions happen by processing incoming actions. We define incoming actions
via State
class methods - the names of such methods must start with receive_
followed by the name of your action. Let’s create a Char
action, that
passes an Op
character type and an integer value for the digit:
In the receive_Char
method we are processing the incoming values and change the state variables accordingly:
- if
op
isOp.digit
, we increase an operand value bydigit * self.factor
and multiply the factor by 10 - if
op
isOp.plus
, we reset theself.factor
and move to the second operand - if
op
isOp.end
, we compute the sum of the operands and go into theStatus.finished
- if the overall status is
finished
we ignore all the incoming actions
You might have noticed that for this transition rules to work, certain constraints on incoming
values of op
and digit
must be satisfied. For example, we cannot receive an action with Op.end
if we have self.status == Status.first
. Similarly, we cannot receive Op.plus
if we Status.second
.
Also, we’d need to constrain the values of the digit
between 0
and 9
.
To add such side conditions on the actions of our model, we can define validate_Char
class method.
The method returns False
when the action is invalid and True
otherwise:
Finally, we need to supply a decomposition scenario. For this, you have to define a global list with the sequence of actions you want for the decomposition. Let’s say we want to see all possible ways our model behaves with at most 7 incoming characters / Char
actions. Then we add the following string to our model:
Decomposing the model
Saving all the model code above into an adder.py
file, we can run the decomposition by passing it
to the imandra.idf.decompose
function. This will start the decomposition job in the cloud and will
return a decomposition handle, which can be used to request the job status:
When decomposition.status()
becomes {"status": "done"}
, we are ready to retrieve the generated Python code.
We can get the Python string with the code by calling decomposition.dumps()
. The generated code is usually
very long, so let us look at a couple of first lines in it:
We can use Python exec
to load the whole code in th current Python session: