Symbolic Simulator Operations¶
In this section, we describe functions specific to Grackle. These are generally used to manipulate formulas or invoke constraint solvers.
symbolic_config¶
Provides the ability to view and update Grackle configuration options.
Syntax
symbolic_config
symbolic_config(option)
x = symbolic_config(option, val)
Description
symbolic_config allow users to view or update the current configuration
options.
- When called with no arguments,
symbolic_configwill print the name of each variable along with the current value that it is set to. - When called with just the first argument
name,symbolic_configwill return the current value of the option. - When called with both arguments,
symbolic_configwill set the current value of the option.
See Simulator Configuration Options for information about the different options.
symbolic_config_help¶
Provides documentation about configuration options.
Syntax
symbolic_config_help(name)
Description
symbolic_config_help provides built-in documentation about the available
configuration variables.
- When called with no arguments
symbolic_config_helpwill print out documentation on all configuration variables in the system, including those which have no values by default (in particular, there may be more options printed here than are displayed bysymbolic_config). - When called with a string argument
name,symbolic_config_helpwill display documentation for the named configuration variable.
assert¶
Syntax
assert(cond, msg)
Description
assert adds the assertion that cond holds in the current
execution context. This assertion will be appended to the condition in
subsequent calls to check_sat. The optional msg argument will
be printed in the event the assertion fails.
In addition to adding cond to the current path condition,
assert generates a proof obligation which states that the
condition holds in the current context. These obligations may later
be examined or proved using the proof_obligations and
prove_obligations procedures.
assume¶
Syntax
assume(cond)
assume adds the assumption that cond holds in the current
execution context. This assumption will be appended to the condition in
subsequent calls to check_sat, as well as to the hypothesis set
of subsequent assert calls.
Unlike assert, assume does not incur any proof obligation.
Instead the predicate is just silently assumed. Thus, assume is
useful for stating preconditions of verifications, whereas assert
is appropriate for safety conditions and postconditions.
symbolic¶
Creates a fresh symbolic variable.
Syntax
x = symbolic(name, dimensions…, class_name)
Description
symbolic creates a new existentially quantified symbolic variable.
All arguments are optional.
If name
nameis provided, it must be a string that names a field used to return the value of struct when calling an SMT solver with functions such ascheck_sat.If
dimensionsare provided, they must contain non-negative integers providing the dimensions of the symbolic array to return. If a single dimensionnis provided, the result is ann*nmatrix. Otherwise, the size of result has the same dimensions as the dimensions.Dimensions must be concrete values. For example, the following code will result in an error:
x = symbolic('x'); a = symbolic('a', x);class_namedefines the type of created variables. It must be one of the following string constants:'integer'creates variables that are integers.'real'creates variables that are real numbers.'complex'creates variables that are complex numbers.'logical'creates variables that are Boolean logical variables.'intN'creates variables that are fixed width signed integers. N must be a positive integer, and the resulting variable’s range is from -2N-1 to 2N-1-1.'uintN'creates variables that are fixed width integers. N must be a non-negative integer, and the resulting variable’s range is from 0 to 2N-1.
If only a single string is provided as an argument, it is interpreted as the class name, and the type is assumed to be
'integer'.
When passed to a solver with check_sat, the variables created by
symbolic are treated as existentially quantified variables, and the
solver will attempt to find an assignment under which the predicate
holds. If the variable has a name and the predicate holds, then
check_sat will return the value found by the solver.
symbolic_array¶
Syntax
x = symbolic_array(name, dimensions…, class_name)
Description
symbolic_array creates a new existentially quantified symbolic array.
All arguments are optional.
If
nameis provided, must be a string that names a field used to return the value of struct when calling an SMT solver with functions such ascheck_sat.If
dimensionsare provided, they must must contain non-negative integers providing the dimensions of the symbolic array to return. If a single dimensionnis provided, the result is ann*nmatrix. Otherwise, the size of result has the same dimensions as the dimensions.Dimensions must be concrete values.
class_namedefines the type of created variables. It must be one of the following string constants:'integer'creates an array of integers.'real'creates an array of real numbers.'complex'creates an array of complex numbers.'logical'creates an array of boolean values.'uintN'creates an array of unsigned machine integers ofNbits.'intN'creates an array of signed machine integers ofNbits.
When passed to a solver with check_sat, the variables created by
symbolic_array are treated as existentially quantified array variables, and the
solver will attempt to find an assignment under which the predicate
holds. If the variable has a name and the predicate holds, then
check_sat will return the value found by the solver.
In contrast to symbolic, the arrays created by symbolic_array
are represented natively using the SMT theory of arrays. This makes
indexing and updating by symbolic values much more efficient than
with the concrete arrays generated by symbolic, but makes
aggregate operations difficult to support.
latch¶
Syntax
x = latch(name, dimensions…, class_name)
Description
latch creates a new value representing part of the state of a transition
system. The arguments following the convention in symbolic and are
all optional.
Expressions created from latch variables are only used to define transition
systems using the write_aig function and not be sent to check_sat.
See write_aig for discssion about how to define the updated latch
variables.
symbolic_arrayfun¶
Syntax
[r1,…,rm] = symbolic_arrayfun(fHandle, a1,…,an)
Description
This operation applies a function, specified by the function handle fHandle,
to each element of an array. The elements from arrays a1,…,an are passed as
input to fHandle and n is the number of parameters that the function
expects. Returns arrays r1,…,rm where m is the number of outputs from
fHandle.
This function is similiar to arrayfun, but is geared towards symbolic
arrays. The simulator will run the function identified by fHandle exactly
one time on symbolic inputs. It then maps the function over the arguments.
The arguments must be builtin types supported by symbolic arrays, and the
return type must be as well.
externalize_array¶
Syntax
r = externalize_array( arr )
Description
Translate a symbolic array into a concrete array containing the same values. This operation breaks up a symbolic formula describing an array into a collection of separate formulas, one for each element of the array. This may be useful because concrete arrays support more kinds of operations than symbolic arrays.
'integer', 'real' and 'complex' symbolic arrays will be
coerced into a concrete array of complex values. 'logical',
'intN', and 'uintN' symbolic arrays will be translated directly into
concrete arrays of the corresponding types.
The arr argument is required and must be a symbolic array. The
return value will be an equivalent concrete array.
internalize_array¶
Syntax
r = internalize_array( arr, class_name )
Description
Translate a concrete array into a symbolic array containing the same values.
This translation constructs an explicit array literal in the SMT theory
of arrays using the element values of the concrete array arr.
The arr argument is required and must be an array of numeric or
logical values. Cell arrays, struct arrays and function handles are
not allowed.
The class_name argument is optional. If not provided, complex
arrays will be translated to complex, logical, intN and uintN arrays
will be translated to corresponding symbolic arrays. However, if
class_name is provided it must be one of the following string values;
the input array will be coerced into a symbolic array of the named type.
'integer''real''complex''logical''intN''uintN'
If class_name is not provided, the arr must be a concrete
array of appropriate type. However, if class_name is provided,
arr may be any array (concrete or symbolic) whose values can be
coerced into the requested type.
forall¶
Syntax
p = forall(name, dimensions…, class_name, pred)
Description
forall allows one to write predicates that contain a universally quantified
variable. The arguments name, dimensions, and class_name are all
interpreted as in symbolic and are all optional. The last argument is
required, and is expected to be a Matlab function that is passed a single
symbolic value, and is expected to return a logical value.
Within the predicate, the symbolic value is treated as a free variable in calls
to check_sat or other functions that build formal models such as
write_smtlib2. This is to allow one to check satisfiability to determine if a
path is feasible.
Outside the predicate, the free variable cannot be directly referenced, and it is an error to assign the symbolic variable to global variables.
The forall predicates introduced by this function are only supported in specific
solvers. abc and yices support the exists-forall fragment, which allows
universally quantified predicates, but does not allow existential predicates to
be nested inside universal predicates. z3 supports nesting via heuristics
but may fail to decide if an expression is true or false.
exists¶
Syntax
p = exists(name, dimensions…, class_name, pred)
Description
exists is the dual of forall, and allows one to write predicates over a
existentially quantified variable. The arguments name, dimensions, and
class_name are all interpreted as in symbolic and are all
optional. The last argument is required, and is expected to be a Matlab function
that is passed a single symbolic value, and is expected to return a logical
value.
Within the predicate, the symbolic value is treated as a free variable in calls
to check_sat or other functions that build formal models such as
write_smtlib2. This is to allow one to check satisfiability to determine if a
path is feasible.
Outside the predicate, the free variable cannot be directly referenced, and it is an error to assign the symbolic variable to global variables.
Support for existential predicates varies depending on the solver. See the discussion in forall for more information.
path_condition¶
Syntax
p = path_condition
Description
path_condition returns the path condition of the
currently-active simulation path. This includes all symbolic branch
conditions in the current path, as well as any symbolic assertions
made along this execution.
check_sat¶
Syntax
[sat,subst,bounds] = check_sat(condition)
Description
check_sat invokes the currently-configured default solver to check
the satisfiability of the given condition in the current execution
context. The first return value is an integer indicating if the given
condition is satisfiable: sat is 0 when the condition is
unsatisfiable, and is 1 if satisfiable. If the problem is
satisfiable and the solver produces models, subst will be a struct
with fields containing an assignment for symbolic values created
previously by the symbolic simulator that are involved in the given
condition. If sat is 0 then subst is an empty struct.
Note: the path condition of the current execution path is
automatically included in the satisfiability predicate passed to the
solver.
Some solvers (in particular, dReal) return only approximately satisfying
solutions on sat answers. Such “satisfying” assignments must be used
with care! In this case, the third argument will be bound to a structure
that maps each symbolic value in the model to a bounding box around the
solution. For a real or complex symbolic value, the structure will
contain the following fields: real_low, real_high, imag_low
and imag_high. Some fields may be missing, which should be
interpreted as no bound in the associated direction (i.e., bounded by
+inf or -inf). Currently, only dReal uses this third argument;
other solvers return only an empty struct.
For example, on the following code:
x = symbolic('x', 1, 2, 'integer');
y = symbolic('y', 'integer');
[sat,subst] = check_sat(x(1) + x(2) > y)
The value sat will be 1, and subst will be a record with two
elements such as
subst =
x: [1, 2]
y: 0
This function is non-deterministic when the function has multiple satisfying assignments.
When calling dReal, the bounding boxes may be used as follows:
symbolic_config('default_solver','dreal');
x = symbolic('x', 'real');
[sat,subst,bounds] = check_sat( x^2 < x );
The values of the substitution and bounding boxes appears as
subst =
x: 2.6621e-04
bounds.x{1} =
real_low: 0
real_high: 5.3243e-04
imag_low: 0
imag_high: 0
reset_cache¶
Syntax
reset_cache
reset_cache clears the current formula cache and sets up a new
one, provided caching is currently enabled. If caching is not
currently enabled, this function has no effect.
proof_obligations¶
Syntax
ps = proof_obligations
proof_obligations simply returns an array of logical values
representing the currently outstanding proof obligations. Each
element of the array represents a separate proof obligation. They are
provided in “negated goal” form, such that the proof is completed iff
the given predicate is unsatisfiable.
Calls to assert and the use of partial operations generate
proof obligations.
prove_obligations¶
Syntax
remaining = prove_obligations
prove_obligations passes each of the currently outstanding proof
obligations to the default solver. Any obligations which are proved
are removed from the list. Any obligations which are disproved are
printed together with their counterexamples.
Afterward, a short summary message is printed which indicates: how many total proofs were attempted; how many were successfully proved; how many were falsified with a counterexample; and how many were inconclusive.
The return value indicates the number of proof obligations that remain
after all proof attempts are complete. This count includes both proof
obligations that were falsified and those which were inconclusive.
In particular, if remaining is 0, all proof attempts were successful.
Calls to assert and the use of partial operations generate
proof obligations.
write_aig¶
Syntax
write_aig(filename, output)
write_aig(filename, output, latches)
Description
This command writes out a combinational or sequential circuit in the bit level
AIG format. Combinational circuits are specified by defining the filename
and output values. The output values are parsed by collecting the symbolic
expressions in output. These values must be either logical values,
fixed precision int or uint values, or cell arrays containing only
logical, or int or uint values.
To define sequential circuit, you should first define latch variables created by the latch function for representing the sequential input latches. There should be one output value for each input latch.
write_dimacs¶
Syntax
write_dimacs(filename, pred)
Description
This writes out the predicate in the DIMACS format used by SAT competitions.
The predicate pred must be formed only from logical and fixed precision
int and uint operations. The DIMACS format only support Boolean values,
and does not support any SMT theories.
write_smtlib2¶
Syntax
write_smtlib2(filename, condition)
Description
write_smtlib2 generates an SMTLIB version 2 file with the given
filename that contains a theorem containing the given condition. The
file will contain free variables for each symbolic variable referred to
in the condition.
write_yices¶
Syntax
write_yices(filename, condition)
Description
write_yices generates an Yices file with the given filename that
contains a theorem containing the given condition. The file will contain
free variables for each symbolic variable referenced in the condition.
find_mus¶
Syntax
mus = find_mus(fixedPredicate, clauses)
Description
Given a fixed predicate and a vector of clauses such that
fixedPredicate & all( clauses ) is unsatisfiable, find a minimal
unsatisfiable subset of the clauses.
The return value of this function is a vector of concrete boolean values of the same length as the clause vector. The clauses that are in the found unsatisfiable subset will be marked with a 1; clauses not in the subset will be marked with a 0.
This function is only available when Grackle is run with the “online” backend command-line flag.
find_all_mus¶
Syntax
mus = find_all_mus(fixedPredicate, clauses, maxCores)
Description
Given a fixed predicate and a vector of clauses such that
fixedPredicate & all( clauses ) is unsatisfiable, find all minimal unsatisfiable
subsets of the clauses, up to the bound maxCores. The return value is a
matrix of Boolean values, where each row in the matrix identifies an MUS,
just as with find_mus.
This function is much like find_mus, except that multiple minimal
unsatisfiable subsets are found. If run with only two arguments, all
MUSs will be computed and returned. If run with three arguments, then at
most maxCores MUSs will be computed.
NOTE: there may be exponentially many MUSs, so take care when asking for all of them.
This function is only available when Grackle is run with the “online” backend command-line flag.
symbolic_fork¶
Syntax
p = symbolic_fork
Description
The symbolic_fork operation creates a separate execution thread that will bypass
the post-dominator-based merging strategy in addition to the current execution
path, which will continue executing as before once the separate execution path
terminates.
The caller can use the return value to determine whether it is in the new
path or the original path. symbolic_fork returns true
to the separate execution path, and false to the original thread.
symbolic_fork cannot currently be used when running grackle interactively.
Note: the way symbolic_fork is implemented causes the true branch
to be executed all the way to program exit before returning to
execute the false branch. Any changes to MATLAB global variables
made on the true branch, as well as any execution result, are
discarded before resuming the false branch.
However, configuration changes made via symbolic_config and any
external side-effects (such as writing files) are retained. The
exit function may be called to immediately end a forked child;
this will only exit from the fork, and not exit the entire interpreter
(unless there is only one remaining forked child).
formula_stats¶
Syntax
formula_stats(formula)
formula_stats(formula, ':file', filename)
Description
This prints statistics about the formula to the screen or to a file if the
:file option is provided. The statistics include the number of
subexpressions in the overall formula, the theories present in the formula,
the number of subexpressions per theory, and how many subexpressions each
line contributed to the formula.
print_formula¶
Syntax
print_formula(formula)
Description
This will print the entire formula for a given expression to the console. This allows one to inspect how terms are represented internally by the simulator.
symbolic_solvers¶
Syntax
r = symbolic_solvers
Description
This returns a cell array containing the names of the supported solvers. This can be used to check whether a given solver is available.
symbolic_fn¶
Syntax
f = symbolic_fn(name, param_types, nargout, fun);
Description
This function symbolically executes the function fun on symbolic inputs whose
types are determined from param_types.
The name parameter should be the name of the function.
The fun parameter should be either a function handle or an anonymous function.
The param_types parameter should be a cell array where element param_types{i}
describes the type of the ith parameter.
The nargout parameter indicates the number of outputs that fun is
expected to return.
Currently, the simulator only allows input parameters to be the primitive types supported by symbolic. To specify a primitive type, the type parameter should be a singleton struct containing the fields:
- A required
typefield which is a string that names one of the symbolic types supported by the symbolic function. - An optional
dimsfield, which defines should be an array of integers defining the dimensions.- If the
dimsfield is empty for a given argument position, then it will be treated as denoting a singleton value. - If the
dimsfield contains a single value n, then it will treat the input as a n by n matrix. - If the
dimfield contains two or more arguments, then it will treat those as the dimensions to an multi-dimensional array or vector.
- If the
symbolic_fn returns a handle to a new function, which given arguments
with the correct types, will symbolically denote the result of applying the
input function fun to those arguments. Semantically, the function r
returned by symbolic_fn is equivalent to the input function on inputs
with the specified types. However calls to r will not actually evaluate the
function, but instead generate a symbolic expression denoting the result.
The primary reason to use this function is to allow functions that are called repeatedly in loop bodies to avoid the overhead of executing the loop body at each iteration. These functions will be used during SMTLIB and Yices output generation as well, potentially allowing much smaller output files and more efficient solving.
Example
This code generates a symbolic function that expects a single integer argument, and a 9x4 array of real values:
params{1}.type = 'integer';
params{2}.type = 'real';
params{2}.dims = [9 4];
f = symbolic_fn(params, @fun);
grackle_simplify_ite¶
Syntax
- ::
- [q] = grackle_simplify_ite(p)
Description
This simplifies the logical expression p by a set of rewrite rules that
distribute over if-then-else expressions. These rewrite rules are not enabled
by default as they can lead to an exponential increase in the size of the terms.
However, for certain problems, we have found dramatic speedup. These rules can
be effective if, for example, there are array access with symbolic indices that
cover a small fraction of the array.
convex_opt¶
Syntax
[feasible subst] = convex_opt(objectiveFunction, constraints)
Description
This performs a gradient descent optimization to find the assignments
for all relevant symbolic values to minimize objectiveFunction subject to
the constraints. The objectiveFunction must be a real value while the
constraints are a single dimension array of reals that implicitly should be
less than or equal to zero.
For example, to find the minimum point on a parabola centered at the origin but
constrained to have x + y >= 7 and x >= 1 one would submit:
x = symbolic('x','real');
y = symbolic('y','real');
obj = x^2;
constrs = [ 7 - x - y, 1 - x];
[s model] = convex_opt(obj,constrs)
This yields an approximation that minimizes for the value of x^2, modulo
constraints, such as:
model =
x: 1.0000
y: 7.1832
Notice both the correctness and termination will suffer if non-convex objectives
or constraints are passed to convex_opt.
WARNING: The convex optimization implementation is currently in an alpha state and should not be used without steps to verify any result.
exit¶
Syntax
exit
exit(exit_code)
Description
This exits the symbolic simulator. By default, the simulator will return exit
code 0. A different exit code can be returned by providing the exit code to
exit, which must be an integer between 0 and 255.
Note: exit will only exit from the current execution. By default,
there is only one execution; however, each invocation of the
symbolic_fork command creates two separate executions. If exit is
used to end an execution, but additional executions remain to be
executed, then the provided exit code is discarded. Only the exit
code used to exit from the final remaining execution will eventually
be returned as the exit code from the Grackle executable.