.. _matlab_api_symbolic_chapter: 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_fun: 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_config`` will 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_config`` will return the current value of the option. - When called with both arguments, ``symbolic_config`` will set the current value of the option. See :ref:`config_chapter` 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_help`` will 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 by ``symbolic_config``). - When called with a string argument ``name``, ``symbolic_config_help`` will 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_fun: 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 ``name`` is 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 as ``check_sat``. - If ``dimensions`` are provided, they must contain non-negative integers providing the dimensions of the symbolic array to return. If a single dimension ``n`` is provided, the result is an ``n*n`` matrix. 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: .. raw:: html
x = symbolic('x');
      a = symbolic('a', x);
- ``class_name`` defines 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 ``name`` is provided, must be a string that names a field used to return the value of struct when calling an SMT solver with functions such as ``check_sat``. - If ``dimensions`` are provided, they must must contain non-negative integers providing the dimensions of the symbolic array to return. If a single dimension ``n`` is provided, the result is an ``n*n`` matrix. Otherwise, the size of result has the same dimensions as the dimensions. Dimensions must be concrete values. - ``class_name`` defines 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 of ``N`` bits. - ``'intN'`` creates an array of signed machine integers of ``N`` bits. 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_fun: 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 :ref:`symbolic_fun` and are all optional. Expressions created from latch variables are only used to define transition systems using the :ref:`write_aig` function and not be sent to ``check_sat``. See :ref:`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_fun: 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 :ref:`symbolic_fun` 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 :ref:`symbolic_fun` 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 :ref:`forall_fun` 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. .. _math_check_sat: 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: 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 :ref:`latch_fun` function for representing the sequential input latches. There should be one `output` value for each input latch. .. _AIG format: http://fmv.jku.at/aiger/ 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_fun: 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_fun: 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_fun: 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 `i`\ th 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 :ref:`symbolic_fun`. To specify a primitive type, the type parameter should be a singleton struct containing the fields: * A required ``type`` field which is a string that names one of the symbolic types supported by the :ref:`symbolic_fun` function. * An optional ``dims`` field, which defines should be an array of integers defining the dimensions. * If the ``dims`` field is empty for a given argument position, then it will be treated as denoting a singleton value. * If the ``dims`` field contains a single value `n`, then it will treat the input as a `n` by `n` matrix. * If the ``dim`` field contains two or more arguments, then it will treat those as the dimensions to an multi-dimensional array or vector. ``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.