Symbolic Simulator Operations

In this section, we describe functions specific to Grackle. These are generally used to manipulate formulas or invoke constraint solvers.

grackle_fresh

uint8_t grackle_fresh_uint8( const char* name )
uint16_t grackle_fresh_uint16( const char* name )
uint32_t grackle_fresh_uint32( const char* name )
uint64_t grackle_fresh_uint64( const char* name )
float grackle_fresh_float( const char* name )
double grackle_fresh_double( const char* name )

This family of functions provides access to fresh symbolic values. When executed in Grackle, these functions generate a fresh uninterpreted constant of the appropriate type, which is returned.

A name may be supplied, which can later be used to refer to the symbolic constant. The name argument must either be a null pointer (in which case no name is assigned), or a nonnull concrete string.

Although these functions only return unsigned integer types, they may be freely cast to signed integers if desired.

grackle_fresh_array

uint8_t*  grackle_fresh_uint8_array ( const char* name, uint32_t num )
uint16_t* grackle_fresh_uint16_array( const char* name, uint32_t num )
uint32_t* grackle_fresh_uint32_array( const char* name, uint32_t num )
uint64_t* grackle_fresh_uint64_array( const char* name, uint32_t num )
float*    grackle_fresh_float_array ( const char* name, uint32_t num )
double*   grackle_fresh_double_array( const char* name, uint32_t num )

This family of functions produce arrays of fresh symbolic values. These functions allocate a fresh array on the heap filled with symbolic values of the appropriate type. The number of elements in the array is determined by the second argument, num.

A name may be supplied, which can later be used to refer to the symbolic array. The name argument must either be a null pointer (in which case no name is assigned), or a nonnull concrete string.

Although these functions only return unsigned integer types, they may be freely cast to signed integers if desired.

If desired, the arrays returned by these functions may safely be passed to free. However, the simulator will retain internal resources related to the fresh constants even if the heap memory in which they were stored is released.

grackle_print_symbolic

void grackle_print_symbolic_uint8( uint8_t sym )
void grackle_print_symbolic_uint16( uint16_t sym )
void grackle_print_symbolic_uint32( uint32_t sym )
void grackle_print_symbolic_uint64( uint64_t sym )
void grackle_print_symbolic_float( float sym )
void grackle_print_symbolic_double( double sym )
void grackle_print_symbolic_pointer( void* sym )

This family of functions prints the symbolic representation of the given symbolic value onto the standard output stream.

grackle_check_sat

struct grackle_model* grackle_check_sat( uint32_t pred )

This function is the primary interface to Grackle’s constraint solving capabilities. Given an integer value to interpret as a boolean (0 for false, nonzero for true), ask the currently-configured solver to check the satisfiability of the given predicate.

If the predicate is unsatisfiable, a null pointer will be returned. Otherwise a nonnull pointer of the opaque type struct grackle_model will be returned. This pointer can be used to access the satisfying assignment calculated by the underlying constraint solver using the grackle_model_get family of functions.

struct grackle_model pointers are opaque handles and should only be passed into further Grackle API functions. Trying to load or store through such pointers will fail. The returned pointer may be passed to free, which will release the simulator resources associated with the model.

grackle_check_sat_raw

struct grackle_model* grackle_check_sat_raw( uint32_t pred )

This function behaves exactly the same as grackle_check_sat, except that the given predicate is passed into the underlying solver totally unmodified. In particular, the current path-conditions are not added to the query.

This is the correct variant of check sat to use if one is manually proving proof obligations retrieved from the grackle_proof_obligations. Otherwise, previously-asserted predicates will be automatically assumed, and the proof obligations will follow vacuously.

grackle_model_get

uint8_t grackle_model_get_uint8( struct grackle_model*, const char* name )
uint16_t grackle_model_get_uint16( struct grackle_model*, const char* name )
uint32_t grackle_model_get_uint32( struct grackle_model*, const char* name )
uint64_t grackle_model_get_uint64( struct grackle_model*, const char* name )
float grackle_model_get_float( struct grackle_model*, const char* name )
double grackle_model_get_double( struct grackle_model*, const char* name )

This family of functions retrieves the concrete assignment made by underlying solver in a previous call to grackle_check_sat for scalar variables (those declared using the grackle_fresh family of functions).

These functions will fail if grackle_model is the null pointer; if name does not refer to some previously generated symbolic value; or if the type of the value referred to by name does not match the type expected by the accessor function.

The model and name arguments to these functions must be concrete.

grackle_model_get_array

uint8_t*  grackle_model_get_uint8_array ( struct grackle_model* mdl, const char* name, uint32_t* num )
uint16_t* grackle_model_get_uint16_array( struct grackle_model* mdl, const char* name, uint32_t* num )
uint32_t* grackle_model_get_uint32_array( struct grackle_model* mdl, const char* name, uint32_t* num )
uint64_t* grackle_model_get_uint64_array( struct grackle_model* mdl, const char* name, uint32_t* num )
float*    grackle_model_get_float_array ( struct grackle_model* mdl, const char* name, uint32_t* num )
double*   grackle_model_get_double_array( struct grackle_model* mdl, const char* name, uint32_t* num )

This family of functions allocate fresh arrays on the heap, into which are copied the concrete values chosen by the underlying solver in a previous call to grackle_check_sat for array variables (those declared using the grackle_fresh_array family of functions). The returned array will have the same size as the original array generated by grackle_fresh_array. If num is a nonnull pointer, the number of elements in the array will be stored into the given pointer.

These functions will fail if grackle_model is the null pointer; if name does not refer to some previously generated symbolic value; or if the type of the value referred to by name does not match the type expected by the accessor function.

The model and name arguments to these functions must be concrete.

The arrays returned by these functions may be safely passed to free.

grackle_print_model

void grackle_print_model( struct grackle_model* )

Print the given model on the standard output stream, using a default formatting scheme.

The model argument to this function must be concrete.

grackle_proof_obligations

uint32_t* grackle_proof_obligations( uint32_t* num )

Construct an array containing the currently-outstanding proof obligations. The number of elements of this array will be written into the memory location pointed to by num.

The returned proof obligations are given in negated-goal form, so that the proof is completed if UNSAT is returned on a check sat query.

NOTE: one should use the grackle_check_sat_raw variant to discharge proof obligations. Otherwise, the current path assumptions may cause the proof to succeed vacuously.

grackle_prove_obligations

uint32_t grackle_prove_obligations( void )

grackle_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 succesfully 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 the return value is 0, all proof attempts were successful.

Calls to grackle_assert and the use of partial operations generate proof obligatons.

grackle_write_smtlib2

void grackle_write_smtlib2( uint32_t pred, const char *filename )

Given an integer pred to interpret as a boolean (in the same way as check_sat), write a SMTLib2-formatted description of the problem into filename.

The filename argument to this function must be concrete. This function will fail if it cannot write the file for some reason.

grackle_write_yices

void grackle_write_yices( uint32_t pred, const char *filename )

Given an integer pred to interpret as a boolean (in the same way as check_sat), write a description of the problem into filename, formatted using the Yices native input language.

The filename argument to this function must be concrete. This function will fail if it cannot write the file for some reason.

grackle_assume

void grackle_assume( uint32_t pred )

Given an integer pred to interpret as a boolean, assume the given predicate. This adds the predicate to the current path condition and does not generate any verification obligations. If pred evaluates to the concrete false value, a warning will be generated.

grackle_assert

void grackle_assert( uint32_t pred )

Given an integer pred to interpret as a boolean, assert the given predicate. This adds the predicate to the current path condition and generates a delayed proof obligation. If pred evaluates to the concrete false value, an error will be generated listing the location of the assertion and the expression that was asserted.