I'm using Player/Stage SWIG generated code from C and now I'm trying to access a structure where a value points to another structure. I wonder how I can extract the array of structures in python.
The C code looks like this:
/** #brief Localization device data. */
typedef struct
{
/** Device info; must be at the start of all device structures. */
playerc_device_t info;
/** Map dimensions (cells). */
int map_size_x, map_size_y;
/** Map scale (m/cell). */
double map_scale;
/** Next map tile to read. */
int map_tile_x, map_tile_y;
/** Map data (empty = -1, unknown = 0, occupied = +1). */
int8_t *map_cells;
/** The number of pending (unprocessed) sensor readings. */
int pending_count;
/** The timestamp on the last reading processed. */
double pending_time;
/** List of possible poses. */
int hypoth_count;
player_localize_hypoth_t *hypoths;
double mean[3];
double variance;
int num_particles;
playerc_localize_particle_t *particles;
} playerc_localize_t;
/** #brief Hypothesis format.
Since the robot pose may be ambiguous (i.e., the robot may at any
of a number of widely spaced locations), the #p localize interface is
capable of returning more that one hypothesis. */
typedef struct player_localize_hypoth
{
/** The mean value of the pose estimate (m, m, rad). */
player_pose2d_t mean;
/** The covariance matrix pose estimate (lower half, symmetric matrix)
(cov(xx) in m$^2$, cov(yy) in $^2$, cov(aa) in rad$^2$,
cov(xy), cov(ya), cov(xa) ). */
double cov[6];
/** The weight coefficient for linear combination (alpha) */
double alpha;
} player_localize_hypoth_t;
The code for python is automatically generated. I'm trying to access the elements in hypoths variable, how can I do this in python? I can access most of the variables. Below is the generated swig code...
class playerc_localize(_object):
__swig_setmethods__ = {}
__setattr__ = lambda self, name, value: _swig_setattr(self, playerc_localize, name, value)
__swig_getmethods__ = {}
__getattr__ = lambda self, name: _swig_getattr(self, playerc_localize, name)
__repr__ = _swig_repr
__swig_setmethods__["info"] = _playerc.playerc_localize_info_set
__swig_getmethods__["info"] = _playerc.playerc_localize_info_get
if _newclass:info = _swig_property(_playerc.playerc_localize_info_get, _playerc.playerc_localize_info_set)
__swig_setmethods__["map_size_x"] = _playerc.playerc_localize_map_size_x_set
__swig_getmethods__["map_size_x"] = _playerc.playerc_localize_map_size_x_get
if _newclass:map_size_x = _swig_property(_playerc.playerc_localize_map_size_x_get, _playerc.playerc_localize_map_size_x_set)
__swig_setmethods__["map_size_y"] = _playerc.playerc_localize_map_size_y_set
__swig_getmethods__["map_size_y"] = _playerc.playerc_localize_map_size_y_get
if _newclass:map_size_y = _swig_property(_playerc.playerc_localize_map_size_y_get, _playerc.playerc_localize_map_size_y_set)
__swig_setmethods__["map_scale"] = _playerc.playerc_localize_map_scale_set
__swig_getmethods__["map_scale"] = _playerc.playerc_localize_map_scale_get
if _newclass:map_scale = _swig_property(_playerc.playerc_localize_map_scale_get, _playerc.playerc_localize_map_scale_set)
__swig_setmethods__["map_tile_x"] = _playerc.playerc_localize_map_tile_x_set
__swig_getmethods__["map_tile_x"] = _playerc.playerc_localize_map_tile_x_get
if _newclass:map_tile_x = _swig_property(_playerc.playerc_localize_map_tile_x_get, _playerc.playerc_localize_map_tile_x_set)
__swig_setmethods__["map_tile_y"] = _playerc.playerc_localize_map_tile_y_set
__swig_getmethods__["map_tile_y"] = _playerc.playerc_localize_map_tile_y_get
if _newclass:map_tile_y = _swig_property(_playerc.playerc_localize_map_tile_y_get, _playerc.playerc_localize_map_tile_y_set)
__swig_setmethods__["map_cells"] = _playerc.playerc_localize_map_cells_set
__swig_getmethods__["map_cells"] = _playerc.playerc_localize_map_cells_get
if _newclass:map_cells = _swig_property(_playerc.playerc_localize_map_cells_get, _playerc.playerc_localize_map_cells_set)
__swig_setmethods__["pending_count"] = _playerc.playerc_localize_pending_count_set
__swig_getmethods__["pending_count"] = _playerc.playerc_localize_pending_count_get
if _newclass:pending_count = _swig_property(_playerc.playerc_localize_pending_count_get, _playerc.playerc_localize_pending_count_set)
__swig_setmethods__["pending_time"] = _playerc.playerc_localize_pending_time_set
__swig_getmethods__["pending_time"] = _playerc.playerc_localize_pending_time_get
if _newclass:pending_time = _swig_property(_playerc.playerc_localize_pending_time_get, _playerc.playerc_localize_pending_time_set)
__swig_setmethods__["hypoth_count"] = _playerc.playerc_localize_hypoth_count_set
__swig_getmethods__["hypoth_count"] = _playerc.playerc_localize_hypoth_count_get
if _newclass:hypoth_count = _swig_property(_playerc.playerc_localize_hypoth_count_get, _playerc.playerc_localize_hypoth_count_set)
__swig_setmethods__["hypoths"] = _playerc.playerc_localize_hypoths_set
__swig_getmethods__["hypoths"] = _playerc.playerc_localize_hypoths_get
if _newclass:hypoths = _swig_property(_playerc.playerc_localize_hypoths_get, _playerc.playerc_localize_hypoths_set)
__swig_setmethods__["mean"] = _playerc.playerc_localize_mean_set
__swig_getmethods__["mean"] = _playerc.playerc_localize_mean_get
if _newclass:mean = _swig_property(_playerc.playerc_localize_mean_get, _playerc.playerc_localize_mean_set)
__swig_setmethods__["variance"] = _playerc.playerc_localize_variance_set
__swig_getmethods__["variance"] = _playerc.playerc_localize_variance_get
if _newclass:variance = _swig_property(_playerc.playerc_localize_variance_get, _playerc.playerc_localize_variance_set)
__swig_setmethods__["num_particles"] = _playerc.playerc_localize_num_particles_set
__swig_getmethods__["num_particles"] = _playerc.playerc_localize_num_particles_get
if _newclass:num_particles = _swig_property(_playerc.playerc_localize_num_particles_get, _playerc.playerc_localize_num_particles_set)
__swig_setmethods__["particles"] = _playerc.playerc_localize_particles_set
__swig_getmethods__["particles"] = _playerc.playerc_localize_particles_get
if _newclass:particles = _swig_property(_playerc.playerc_localize_particles_get, _playerc.playerc_localize_particles_set)
def __init__(self, *args):
this = _playerc.new_playerc_localize(*args)
try: self.this.append(this)
except: self.this = this
def destroy(self): return _playerc.playerc_localize_destroy(self)
def subscribe(self, *args): return _playerc.playerc_localize_subscribe(self, *args)
def unsubscribe(self): return _playerc.playerc_localize_unsubscribe(self)
def set_pose(self, *args): return _playerc.playerc_localize_set_pose(self, *args)
def get_particles(self): return _playerc.playerc_localize_get_particles(self)
__swig_destroy__ = _playerc.delete_playerc_localize
__del__ = lambda self : None;
playerc_localize_swigregister = _playerc.playerc_localize_swigregister
playerc_localize_swigregister(playerc_localize)
Everything compiles in SWIG, but now my python code gives errors. I know that the alpha line may be wrong, but I get the same error for both lines:
LOC = playerc_localize(CON, 0)
if LOC.subscribe(PLAYERC_OPEN_MODE) != 0:
raise playerc_error_str()
CON.read()
# This should work right? If I omit the index,
# then I get the first value from the array.
print LOC.get_hypoth(0).alpha
print LOC.get_hypoth(1)
I get the following errors in python:
print LOC.get_hypoth(1).alpha
File "/usr/local/lib/python2.7/dist-packages/playerc.py", line 7413, in get_hypoth
return _playerc._playerc_localize_get_hypoth(self, index)
TypeError: in method '_playerc_localize_get_hypoth', argument 1 of type 'playerc_localize_t *'
PART II
I've come around another problem, again I'm trying to access some values but it doesn't work and I can't figure out whats wrong. I'm trying to access waypoints in the planner device:
PLN = playerc_planner(CON, 0)
if PLN.subscribe(PLAYERC_OPEN_MODE) != 0:
raise playerc_error_str()
# saves the waypoints in PLN.waypoints
PLN.get_waypoints()
# gives: <Swig Object of type 'double (*)[3]' at 0x38105d0>
# if i try accessing members of it using (0) or [0] or something I get errors
print PLN.waypoints
Now the related files and parts: in playerc.py
class playerc_planner(_object):
...
__swig_setmethods__["waypoints"] = _playerc.playerc_planner_waypoints_set
__swig_getmethods__["waypoints"] = _playerc.playerc_planner_waypoints_get
if _newclass:waypoints = _swig_property(_playerc.playerc_planner_waypoints_get, _playerc.playerc_planner_waypoints_set)
The part in playerc_wrap.i:
%header
%{
#define new_playerc_planner playerc_planner_create
#define del_playerc_planner playerc_planner_destroy
typedef playerc_planner_t playerc_planner;
%}
typedef struct
{
playerc_device info;
int path_valid;
int path_done;
double px, py, pa;
double gx, gy, ga;
double wx, wy, wa;
int curr_waypoint;
int waypoint_count;
double (*waypoints)[3];
%extend
{
playerc_planner (playerc_client *client, int index);
void destroy(void);
int subscribe (int access);
int unsubscribe (void);
int set_cmd_pose (double gx, double gy, double ga);
int get_waypoints (void);
int enable (int state);
}
} playerc_planner;
And the related part in playerc_wrap.h:
typedef struct
{
playerc_device_t info;
int path_valid;
int path_done;
double px, py, pa;
double gx, gy, ga;
double wx, wy, wa;
int curr_waypoint;
int waypoint_count;
double (*waypoints)[3];
} playerc_planner_t;
PLAYERC_EXPORT playerc_planner_t *playerc_planner_create(playerc_client_t *client, int index);
PLAYERC_EXPORT void playerc_planner_destroy(playerc_planner_t *device);
PLAYERC_EXPORT int playerc_planner_subscribe(playerc_planner_t *device, int access);
PLAYERC_EXPORT int playerc_planner_unsubscribe(playerc_planner_t *device);
PLAYERC_EXPORT int playerc_planner_set_cmd_pose(playerc_planner_t *device,
double gx, double gy, double ga);
PLAYERC_EXPORT int playerc_planner_get_waypoints(playerc_planner_t *device);
PLAYERC_EXPORT int playerc_planner_enable(playerc_planner_t *device, int state);
So I'm again lost in how to access the waypoints in this double(*)[3] variable, I tried some stuff but it all fails to compile in SWIG. Again thanks!
It looks like those dynamically sized arrays are not properly wrapped in libcplayer's SWIG interface file. The LOC.hypoths accessor will probably just give you the first element in the array, not any of the others.
I think the easiest thing to do would be to add something like this to playerc.i and regenerate the SWIG bindings:
%inline %{
player_localize_hypoth_t *_playerc_localize_get_hypoth(playerc_localize *localize, int index)
{
return &(localize->hypoths[index]);
}
%}
%extend playerc_localize {
%pythoncode %{
def get_hypoth(self, index):
return _playerc._playerc_localize_get_hypoth(self, index)
%}
}
Then you should be able to access element number n in Python with LOC.get_hypoth(n).
References:
SWIG 2.0 manual section 34.6
libplayerc/bindings/python/playerc.i
libplayerc/playerc.h
libplayerc/dev_localize.c
libplayerc/bindings/python/playerc_swig_parse.py
Notes:
The name of the struct is playerc_localize instead of playerc_localize_t because of the regular expressions applied to the interface file by the playerc_swig_parse.py script.
Part II
A similar problem. Again, there might be a cleverer way to do this, but this simple way is nice and clean and there's no need to try and make it any more complicated than it already is:
%inline %{
double _playerc_planner_get_waypoint_coord(playerc_planner *planner, int index, int coord)
{
return planner->waypoints[index][coord];
}
%}
%extend playerc_planner {
%pythoncode %{
def get_waypoint(self, index):
x = _playerc._playerc_planner_get_waypoint_coord(self, index, 0)
y = _playerc._playerc_planner_get_waypoint_coord(self, index, 1)
z = _playerc._playerc_planner_get_waypoint_coord(self, index, 2)
return (x, y, z)
%}
}
Then you should be able to access waypoint number n in Python like this:
(x, y, z) = PLN.get_waypoint(n)
Related
I'm trying to get an array of tensorflow box predictions from C++ to golang, but I'm not able to do it no matter what I do. I have a GO program that calls a function that does tensorflow detections in C++ using cgo. This all works and I'm able to get the predictions in C++. The problem is to transfer these predictions into GO as an array of 100 structs that each hold one prediction.
I'm able to set a pointer in GO and use this pointer address to set one struct in C++. The code for this is seen below.
I want to set an array of structs in C++ and retreive this array in GO. I thought it should be easy to just use the same pointer address as earlier and use this as the address for my C++ array. Then I could restore the struct from the pointer in GO. Does anyone have a solution for this?
GO
type PredictResult struct {
Loc [4]float32
Score int
Label int
}
var predictions PredictResult
predictions_ptr := unsafe.Pointer(&predictions)
C.LIB_predict(predictions_ptr)
fmt.Println("GO predictions; ", predictions)
bridge.hpp
struct PredictResult{
float Loc[4];
int64_t Score;
int64_t Label;
};
void LIB_predict(void* predictions);
bridge.cpp
void LIB_predict(void* predictions){
PredictResult *p = (PredictResult*)predictions;
p->Score = 6;
p->Label = 95;
}
Prints:
GO predictions; {[0 0 0 0] 6 95}
Assuming your C function returns the array as PredictResult* and assuming you know the length of the returned array (in the example below I assume 10, but you can replace it by whatever works), this approach should work:
// #include <stdio.h>
// #include <stdlib.h>
//
// typedef struct PredictResult {
// float Loc[4];
// int64_t Score;
// int64_t Label;
// } PredictResult;
//
// PredictResult* getOneResult() {
// PredictResult* p = (PredictResult*)calloc(1, sizeof(PredictResult));
// p->Score = 10;
// p->Label = 99;
// p->Loc[1] = 2.5;
// p->Loc[3] = 3.5;
// return p;
// }
//
// PredictResult* getTenResults() {
// PredictResult* parr = (PredictResult*)calloc(10, sizeof(PredictResult));
// parr[0].Score = 10;
// parr[0].Label = 99;
// parr[0].Loc[1] = 2.5;
// parr[0].Loc[3] = 3.5;
//
// parr[4].Score = 44;
// parr[4].Label = 123;
// parr[4].Loc[1] = 12.25;
// parr[4].Loc[3] = -40.5;
// return parr;
// }
//
//
import "C"
type PredictResult C.struct_PredictResult
func main() {
p := C.getOneResult()
if p == nil {
log.Fatal("got nil")
}
pp := (*PredictResult)(p)
fmt.Println(pp)
parr := C.getTenResults()
if parr == nil {
log.Fatal("got nil")
}
pslice := (*[1 << 28]PredictResult)(unsafe.Pointer(parr))[:10:10]
fmt.Println(pslice)
}
What you'll be most interested in is how the result of getTenResults is converted to a Go slice of the appropriate struct type. This is employing the technique recommended on the Go wiki.
Depending on the exact signature of your C function you may need to write a "bridge" function in the import "C" part to provide the data as convenient to Go, but this is the basic gist of it.
As an alternative, if you wish to allocate the slice on the Go side and pass in a pointer to C to populate, you can do this:
// void PopulateTenResults(void* arr) {
// PredictResult* parr = (PredictResult*)arr;
// parr[1].Score = 210;
// parr[1].Label = 299;
// parr[1].Loc[1] = 22.5;
// parr[1].Loc[3] = 23.5;
//
// parr[8].Score = 344;
// parr[8].Label = 3123;
// parr[8].Loc[1] = 312.25;
// parr[8].Loc[3] = -340.5;
// }
//
//
import "C"
And then in Go do:
prslice := make([]PredictResult, 10)
C.PopulateTenResults(unsafe.Pointer(&prslice[0]))
fmt.Println(prslice)
Of course the hard-coded 10 is just for simplicity here; you could pass the length of arr as a parameter to C.
You can pass a pointer to the first element in a slice and the length of the slice to C++ and treat it like a C-style array.
Am using Rcpp packages and can get my C function to compile and run in R, but now I want to return a large, user-defined data structure to R. The fields in the structure are either numbers or strings - no new or odd types within the structure. The example below is simplified and doesn't compile, but it conveys the idea of my problem.
typedef struct {
char* firstname[128];
char* lastname[128];
int nbrOfSamples;
} HEADER_INFO;
// [[Rcpp::export]]
HEADER_INFO* read_header(Rcpp::StringVector strings) {
FILE *fp;
MEF_HEADER_INFO *header;
char * filename = (char*)(strings(0));
char * password = (char*)(strings(1));
header = (HEADER_INFO*)malloc(sizeof(HEADER_INFO));
memset(header, 0, sizeof(HEADER_INFO));
fp = fopen(filename, "r");
(void)read_header(header, password);
return header;
}
I'm pretty sure that I could package the entries in the header back into a StringVector, but that seems like a brute-force approach. My question is whether a more elegant solution exists. It is not clear to me what form such a structure would even have in R: a named List?
Thanks!
The right structure in R depends on what your struct looks like exactly. A named list is the most general one. Here a simple sample implementation for a wrap function as referred to in the comments:
#include <RcppCommon.h>
typedef struct {
char* firstname[128];
char* lastname[128];
int nbrOfSamples;
} HEADER_INFO;
namespace Rcpp {
template <>
SEXP wrap(const HEADER_INFO& x);
}
#include <Rcpp.h>
namespace Rcpp {
template <>
SEXP wrap(const HEADER_INFO& x) {
Rcpp::CharacterVector firstname(x.firstname, x.firstname + x.nbrOfSamples);
Rcpp::CharacterVector lastname(x.lastname, x.lastname + x.nbrOfSamples);
return Rcpp::wrap(Rcpp::List::create(Rcpp::Named("firstname") = firstname,
Rcpp::Named("lastname") = lastname,
Rcpp::Named("nbrOfSamples") = Rcpp::wrap(x.nbrOfSamples)));
};
}
// [[Rcpp::export]]
HEADER_INFO getHeaderInfo() {
HEADER_INFO header;
header.firstname[0] = (char*)"Albert";
header.lastname[0] = (char*)"Einstein";
header.firstname[1] = (char*)"Niels";
header.lastname[1] = (char*)"Bohr";
header.firstname[2] = (char*)"Werner";
header.lastname[2] = (char*)"Heisenberg";
header.nbrOfSamples = 3;
return header;
}
/*** R
getHeaderInfo()
*/
Output:
> getHeaderInfo()
$firstname
[1] "Albert" "Niels" "Werner"
$lastname
[1] "Einstein" "Bohr" "Heisenberg"
$nbrOfSamples
[1] 3
However, for this particular case a data.frame would be more natural to use, which can be achieved by replacing above wrap with:
template <>
SEXP wrap(const HEADER_INFO& x) {
Rcpp::CharacterVector firstname(x.firstname, x.firstname + x.nbrOfSamples);
Rcpp::CharacterVector lastname(x.lastname, x.lastname + x.nbrOfSamples);
return Rcpp::wrap(Rcpp::DataFrame::create(Rcpp::Named("firstname") = firstname,
Rcpp::Named("lastname") = lastname));
};
Output:
> getHeaderInfo()
firstname lastname
1 Albert Einstein
2 Niels Bohr
3 Werner Heisenberg
This is sort of a design doubt .
Scenario: I have an array which contain some integer elements . This array is populated by 1 module (.so) in my code base say X. It is then shared to another module say Y (.so) . At run time X module identifies that module Y would need to work on few fields of the array and modify it and that was the reason X shared the array to Y . ( Both these so are consumed into one binary .)
Once Y returns the module X prints the array .
Problem : How can I enforce programatically that module Y does not modify any other array index other than the one identified by X . SInce the whole array is passed between modules i cant make it const as then Y would not be able to change any field . You can say i want to enforce const-ness for few fields identified at run time .
How about this:
template <class T> class CProtectedArray {
private:
T* m_aItems;
unsigned int* m_aMask;
public:
CProtectedArray(int cElem, bool fInitialProtect) : m_aItems(NULL) {
int cbElem = sizeof(T)*cElem;
int cbMask = sizeof(int)*(cElem+31)/32;
m_aItems = (T*)malloc(cbElem + cbMask);
m_aMask = (unsigned int*)(m_aItems + cElem);
memset(m_aItems, 0, cbElem);
memset(m_aMask, fInitialProtect ? -1 : 0, cbMask);
}
~CProtectedArray() {
if (m_aItems)
free(m_aItems);
}
bool IsProtected(int iItem) { return !!(m_aMask[iItem>>5] & (1<<(iItem&31))); }
void Protect(int iItem) { m_aMask[iItem>>5] |= 1<<(iItem&31); }
void UnProtect(int iItem) { m_aMask[iItem>>5] &= ~(1<<(iItem&31)); }
void Set(int iItem, T val) {
if (!IsProtected(iItem))
m_aItems[iItem] = val;
}
};
int main(int argc, char* argv[])
{
CProtectedArray<int> A(100, true);
bool f = A.IsProtected(30); // f = true
A.Set(30, 23); // nothing happens
A.UnProtect(30);
f = A.IsProtected(30); // f = false
A.Set(30, 24); // sets element 30 to 24
A.Protect(30);
f = A.IsProtected(30); // f = true
A.Set(30, 25); // nothing happens
}
This question is a follow up to the following question.
Procedural Attachment in Z3
I have a predicate (I use the name "heavier" in this case) over two integers that I need to evaluate using a custom algorithm. I have written the following piece of code to do it. But I see that the parameters that get passed into the function CMTh_reduce_app() are not actual integers, but consts of type integer. What I need is 2 integers, so that I can evaluate the predicate and return the result (The operations done in the function CMTh_reduce_app() right now are meaningless).
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdarg.h>
#include<memory.h>
#include<z3.h>
#include "z3++.h"
#include<iostream>
using namespace z3;
using namespace std;
struct _CMTheoryData {
Z3_func_decl heavier;
};
typedef struct _CMTheoryData CMTheoryData;
Z3_context ctx;
//Exit function
void exitf(const char* message)
{
fprintf(stderr,"BUG: %s.\n", message);
exit(1);
}
//Check and print model if available
void check(Z3_context ctx)
{
Z3_model m = 0;
Z3_lbool result = Z3_check_and_get_model(ctx, &m);
switch (result) {
case Z3_L_FALSE:
printf("unsat\n");
break;
case Z3_L_UNDEF:
printf("unknown\n");
printf("potential model:\n%s\n", Z3_model_to_string(ctx, m));
break;
case Z3_L_TRUE:
printf("sat\n%s\n", Z3_model_to_string(ctx, m));
break;
}
if (m) {
Z3_del_model(ctx, m);
}
}
//Create logical context. Enable model generation, and set error handler
void error_handler(Z3_error_code e)
{
printf("Error code: %d\n", e);
exitf("incorrect use of Z3");
}
Z3_context mk_context_custom(Z3_config cfg, Z3_error_handler err)
{
Z3_context ctx;
Z3_set_param_value(cfg, "MODEL", "true");
ctx = Z3_mk_context(cfg);
#ifdef TRACING
Z3_trace_to_stderr(ctx);
#endif
Z3_set_error_handler(ctx, err);
return ctx;
}
Z3_context mk_context()
{
Z3_config cfg;
Z3_context ctx;
cfg = Z3_mk_config();
ctx = mk_context_custom(cfg, error_handler);
Z3_del_config(cfg);
return ctx;
}
//Shortcut for binary fn application
Z3_ast mk_binary_app(Z3_context ctx, Z3_func_decl f, Z3_ast x, Z3_ast y)
{
Z3_ast args[2] = {x, y};
return Z3_mk_app(ctx, f, 2, args);
}
//Shortcut to create an int
Z3_ast mk_int(Z3_context ctx, int v)
{
Z3_sort ty = Z3_mk_int_sort(ctx);
return Z3_mk_int(ctx, v, ty);
}
Z3_ast mk_var(Z3_context ctx, const char * name, Z3_sort ty)
{
Z3_symbol s = Z3_mk_string_symbol(ctx, name);
return Z3_mk_const(ctx, s, ty);
}
Z3_ast mk_int_var(Z3_context ctx, const char * name)
{
Z3_sort ty = Z3_mk_int_sort(ctx);
return mk_var(ctx, name, ty);
}
//Callback when final check is to be carried out
Z3_bool CMTh_final_check(Z3_theory t) {
printf("Final check\n");
return Z3_TRUE;
}
//Callback when theory is to be deleted
void CMTh_delete(Z3_theory t) {
CMTheoryData * td = (CMTheoryData *)Z3_theory_get_ext_data(t);
printf("Delete\n");
free(td);
}
//Callback to reduce a function application(definition of custom functions, predicates)
Z3_bool CMTh_reduce_app(Z3_theory t, Z3_func_decl d, unsigned n, Z3_ast const args[], Z3_ast * result) {
CMTheoryData * td = (CMTheoryData*)Z3_theory_get_ext_data(t);
cout<<Z3_ast_to_string(ctx, args[0])<<' '<<Z3_ast_to_string(ctx,args[1])<<endl;
if (d == td->heavier) {
cout<<"Reducing the fn \'heavier\'"<<endl;
if(Z3_is_eq_ast(ctx,mk_int(ctx, 1),args[0])||Z3_is_eq_ast(ctx,mk_int(ctx,2),args[0]))
{
*result = Z3_mk_true(Z3_theory_get_context(t));
return Z3_TRUE;;
}
else
{
*result = Z3_mk_false(Z3_theory_get_context(t));
return Z3_TRUE;;
}
}
return Z3_FALSE; // failed to simplify
}
Z3_theory mk_cm_theory(Z3_context ctx) {
Z3_sort heavier_domain[2];
Z3_symbol heavier_name = Z3_mk_string_symbol(ctx, "heavier");
Z3_sort B = Z3_mk_bool_sort(ctx);
CMTheoryData * td = (CMTheoryData*)malloc(sizeof(CMTheoryData));
Z3_theory Th = Z3_mk_theory(ctx, "cm_th", td);
heavier_domain[0] = Z3_mk_int_sort(ctx);
heavier_domain[1] = Z3_mk_int_sort(ctx);
td->heavier = Z3_theory_mk_func_decl(ctx, Th, heavier_name, 2, heavier_domain, B); //context, theory, name_of_fn, number of arguments, argument type list, return type
Z3_set_delete_callback(Th, CMTh_delete);
Z3_set_reduce_app_callback(Th, CMTh_reduce_app);
Z3_set_final_check_callback(Th, CMTh_final_check);
return Th;
}
main()
{
Z3_ast a_ast, b_ast, c_ast, f1, f3, r;
Z3_sort i;
Z3_pattern p;
Z3_app bound[2];
Z3_theory Th;
CMTheoryData * td;
printf("\nCustom theory example\n");
ctx = mk_context();
Th = mk_cm_theory(ctx);
td = (CMTheoryData*)Z3_theory_get_ext_data(Th);
a_ast = mk_int_var(ctx, "a");
b_ast = mk_int_var(ctx, "b");
bound[0] = (Z3_app)a_ast;
f1=mk_binary_app(ctx, td->heavier, a_ast, b_ast);
r= Z3_mk_exists_const(ctx, 0, 1, bound, 0, 0,f1);
printf("assert axiom:\n%s\n", Z3_ast_to_string(ctx, r));
Z3_assert_cnstr(ctx, r);
check(ctx);
}
I know the user theory plugin is not supported anymore, but I really need to get this working, so if I could get any information, it would be really helpful. I tried looking at the source code, but I didn't know where to get started with building new theories into it. So, I'd appreciate some help with the theory plugin.
Models are not going to be accessible to you from the
abstraction that the deprecated theory plugin provides.
The problem is going to be that models are constructed later in the game.
It would require rewriting some of the internals to accommodate this
(it is not impossible, but a very fair chunk of work).
My impression is that it would be simpler to use just the basic interaction
with Z3 where you declare the predicates as uninterpreted, check for SAT.
Then if the current constraints are satisfiable,
use the current model to evaluate arguments. If you have values, that contradict your
built-in procedural attachment, then assert new facts that rule these values out (and as many
other infeasible values as possible). I call this the "lazy loop approach".
This interaction model corresponds to how SMT solvers can use
SAT solvers without providing theory propagation (propagating truth values
when new atoms are assigned). You would have to do a bit more work during
conflict analysis/resolution in order to produce strong lemmas. So a hybrid
between the built-in theory and the lazy loop approach may in the end work out.
But before getting there I suggest to just use Z3 as is and use the current model to
calculate new blocking clauses.
Of course you lose something: instantiation of quantifiers will proceed somewhat eagerly
and it could very well be the case that this lazy loop approach will not work well in the presence
of quantifiers.
I am using libsvm version 3.16. I have done some training in Matlab, and created a model. Now I would like to save this model to disk and load this model in my C++ program. So far I have found the following alternatives:
This answer explains how to save a model from C++, which is based on this website. Not exactly what I need, but could be adapted. (This requires development time).
I could find the best training parameters (kernel,C) in Matlab and re-train everything in C++. (Will require doing the training in C++ each time I change a parameter. It's not scalable).
Thus, both of these options are not satisfactory,
Does anyone have an idea?
My solution was to retrain in C++ because I couldn't find a nice way to directly save the model. Here's my code. You'll need to adapt it and clean it up a bit. The biggest change you'll have to make it not hard coding the svm_parameter values like I did. You'll also have to replace FilePath with std::string. I'm copying, pasting and making small edits here in SO so the formatting won't e perfect:
Used like this:
auto targetsPath = FilePath("targets.txt");
auto observationsPath = FilePath("observations.txt");
auto targetsMat = MatlabMatrixFileReader::Read(targetsPath, ',');
auto observationsMat = MatlabMatrixFileReader::Read(observationsPath, ',');
auto v = MiscVector::ConvertVecOfVecToVec(targetsMat);
auto model = SupportVectorRegressionModel{ observationsMat, v };
std::vector<double> observation{ { // 32 feature observation
0.883575729725847,0.919446119013878,0.95359403450317,
0.968233630936732,0.91891307107125,0.887897763183844,
0.937588566544751,0.920582702918882,0.888864454119387,
0.890066735260163,0.87911085669864,0.903745573664995,
0.861069296586979,0.838606194934074,0.856376230548304,
0.863011311537075,0.807688936997926,0.740434984165146,
0.738498042748759,0.736410940165691,0.697228384912424,
0.608527698289016,0.632994967880269,0.66935784966765,
0.647761430696238,0.745961037635717,0.560761134660957,
0.545498063585615,0.590854855113663,0.486827902942118,
0.187128866890822,- 0.0746523069562551
} };
double prediction = model.Predict(observation);
miscvector.h
static vector<double> ConvertVecOfVecToVec(const vector<vector<double>> &mat)
{
vector<double> targetsVec;
targetsVec.reserve(mat.size());
for (size_t i = 0; i < mat.size(); i++)
{
targetsVec.push_back(mat[i][0]);
}
return targetsVec;
}
libsvmtargetobjectconvertor.h
#pragma once
#include "machinelearning.h"
struct svm_node;
class LibSvmTargetObservationConvertor
{
public:
svm_node ** LibSvmTargetObservationConvertor::ConvertObservations(const vector<MlObservation> &observations, size_t numFeatures) const
{
svm_node **svmObservations = (svm_node **)malloc(sizeof(svm_node *) * observations.size());
for (size_t rowI = 0; rowI < observations.size(); rowI++)
{
svm_node *row = (svm_node *)malloc(sizeof(svm_node) * numFeatures);
for (size_t colI = 0; colI < numFeatures; colI++)
{
row[colI].index = colI;
row[colI].value = observations[rowI][colI];
}
row[numFeatures].index = -1; // apparently needed
svmObservations[rowI] = row;
}
return svmObservations;
}
svm_node* LibSvmTargetObservationConvertor::ConvertMatToSvmNode(const MlObservation &observation) const
{
size_t numFeatures = observation.size();
svm_node *obsNode = (svm_node *)malloc(sizeof(svm_node) * numFeatures);
for (size_t rowI = 0; rowI < numFeatures; rowI++)
{
obsNode[rowI].index = rowI;
obsNode[rowI].value = observation[rowI];
}
obsNode[numFeatures].index = -1; // apparently needed
return obsNode;
}
};
machinelearning.h
#pragma once
#include <vector>
using std::vector;
using MlObservation = vector<double>;
using MlTarget = double;
//machinelearningmodel.h
#pragma once
#include <vector>
#include "machinelearning.h"
class MachineLearningModel
{
public:
virtual ~MachineLearningModel() {}
virtual double Predict(const MlObservation &observation) const = 0;
};
matlabmatrixfilereader.h
#pragma once
#include <vector>
using std::vector;
class FilePath;
// Matrix created with command:
// dlmwrite('my_matrix.txt', somematrix, 'delimiter', ',', 'precision', 15);
// In these files, each row is a matrix row. Commas separate elements on a row.
// There is no space at the end of a row. There is a blank line at the bottom of the file.
// File format:
// 0.4,0.7,0.8
// 0.9,0.3,0.5
// etc.
static class MatlabMatrixFileReader
{
public:
static vector<vector<double>> Read(const FilePath &asciiFilePath, char delimiter)
{
vector<vector<double>> values;
vector<double> valueline;
std::ifstream fin(asciiFilePath.Path());
string item, line;
while (getline(fin, line))
{
std::istringstream in(line);
while (getline(in, item, delimiter))
{
valueline.push_back(atof(item.c_str()));
}
values.push_back(valueline);
valueline.clear();
}
fin.close();
return values;
}
};
supportvectorregressionmodel.h
#pragma once
#include <vector>
using std::vector;
#include "machinelearningmodel.h"
#include "svm.h" // libsvm
class FilePath;
class SupportVectorRegressionModel : public MachineLearningModel
{
public:
SupportVectorRegressionModel::~SupportVectorRegressionModel()
{
svm_free_model_content(model_);
svm_destroy_param(¶m_);
svm_free_and_destroy_model(&model_);
}
SupportVectorRegressionModel::SupportVectorRegressionModel(const vector<MlObservation>& observations, const vector<MlTarget>& targets)
{
// assumes all observations have same number of features
size_t numFeatures = observations[0].size();
//setup targets
//auto v = ConvertVecOfVecToVec(targetsMat);
double *targetsPtr = const_cast<double *>(&targets[0]); // why aren't the targets const?
LibSvmTargetObservationConvertor conv;
svm_node **observationsPtr = conv.ConvertObservations(observations, numFeatures);
// setup observations
//svm_node **observations = BuildObservations(observationsMat, numFeatures);
// setup problem
svm_problem problem;
problem.l = targets.size();
problem.y = targetsPtr;
problem.x = observationsPtr;
// specific to out training sets
// TODO: This is hard coded.
// Bust out these values for use in constructor
param_.C = 0.4; // cost
param_.svm_type = 4; // SVR
param_.kernel_type = 2; // radial
param_.nu = 0.6; // SVR nu
// These values are the defaults used in the Matlab version
// as found in svm_model_matlab.c
param_.gamma = 1.0 / (double)numFeatures;
param_.coef0 = 0;
param_.cache_size = 100; // in MB
param_.shrinking = 1;
param_.probability = 0;
param_.degree = 3;
param_.eps = 1e-3;
param_.p = 0.1;
param_.shrinking = 1;
param_.probability = 0;
param_.nr_weight = 0;
param_.weight_label = NULL;
param_.weight = NULL;
// suppress command line output
svm_set_print_string_function([](auto c) {});
model_ = svm_train(&problem, ¶m_);
}
double SupportVectorRegressionModel::Predict(const vector<double>& observation) const
{
LibSvmTargetObservationConvertor conv;
svm_node *obsNode = conv.ConvertMatToSvmNode(observation);
double prediction = svm_predict(model_, obsNode);
return prediction;
}
SupportVectorRegressionModel::SupportVectorRegressionModel(const FilePath & modelFile)
{
model_ = svm_load_model(modelFile.Path().c_str());
}
private:
svm_model *model_;
svm_parameter param_;
};
Option 1 is actually pretty reasonable. If you save the model in libsvm's C format through matlab, then it is straightforward to work with the model in C/C++ using functions provided by libsvm. Trying to work with matlab-formatted data in C++ will probably be much more difficult.
The main function in "svm-predict.c" (located in the root directory of the libsvm package) probably has most of what you need:
if((model=svm_load_model(argv[i+1]))==0)
{
fprintf(stderr,"can't open model file %s\n",argv[i+1]);
exit(1);
}
To predict a label for example x using the model, you can run
int predict_label = svm_predict(model,x);
The trickiest part of this will be to transfer your data into the libsvm format (unless your data is in the libsvm text file format, in which case you can just use the predict function in "svm-predict.c").
A libsvm vector, x, is an array of struct svm_node that represents a sparse array of data. Each svm_node has an index and a value, and the vector must be terminated by an index that is set to -1. For instance, to encode the vector [0,1,0,5], you could do the following:
struct svm_node *x = (struct svm_node *) malloc(3*sizeof(struct svm_node));
x[0].index=2; //NOTE: libsvm indices start at 1
x[0].value=1.0;
x[1].index=4;
x[1].value=5.0;
x[2].index=-1;
For SVM types other than the classifier (C_SVC), look at the predict function in "svm-predict.c".