Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix cgo pointer issues #167

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
14 changes: 7 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
language: go
dist: bionic
go:
- "1.9"
- "1.10"
- "1.11"
- "1.12"
- "1.13"
- "1.14"
arch:
- amd64
- arm64
env:
# Temporary workaround for go 1.6
- GODEBUG=cgocheck=0
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq libatlas-base-dev
- cd /tmp && wget http://www.csie.ntu.edu.tw/~cjlin/liblinear/oldfiles/liblinear-1.94.tar.gz && tar xf liblinear-1.94.tar.gz && cd liblinear-1.94 && make lib && sudo install -vm644 linear.h /usr/include && sudo install -vm755 liblinear.so.1 /usr/lib && sudo ln -sfv liblinear.so.1 /usr/lib/liblinear.so
- cd /tmp && wget https://www.csie.ntu.edu.tw/~cjlin/liblinear/oldfiles/liblinear-1.94.tar.gz && tar xf liblinear-1.94.tar.gz && cd liblinear-1.94 && make lib && sudo install -vm644 linear.h /usr/include && sudo install -vm755 liblinear.so.1 /usr/lib && sudo ln -sfv liblinear.so.1 /usr/lib/liblinear.so
- cd $TRAVIS_BUILD_DIR
install:
- go get github.com/smartystreets/goconvey/convey
Expand Down
56 changes: 30 additions & 26 deletions ensemble/multisvc_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package ensemble

import (
"io/ioutil"
"testing"

"github.com/sjwhitworth/golearn/base"
"github.com/sjwhitworth/golearn/evaluation"
. "github.com/smartystreets/goconvey/convey"
"io/ioutil"
"testing"
)

func TestMultiSVMUnweighted(t *testing.T) {
Expand All @@ -14,40 +15,43 @@ func TestMultiSVMUnweighted(t *testing.T) {
So(err, ShouldBeNil)
X, Y := base.InstancesTrainTestSplit(inst, 0.4)

m := NewMultiLinearSVC("l1", "l2", true, 1.0, 1e-4, nil)
m.Fit(X)

Convey("Predictions should work...", func() {
predictions, err := m.Predict(Y)
So(err, ShouldEqual, nil)
cf, err := evaluation.GetConfusionMatrix(Y, predictions)
So(err, ShouldEqual, nil)
So(evaluation.GetAccuracy(cf), ShouldBeGreaterThan, 0.70)
})

Convey("Saving should work...", func() {
f, err := ioutil.TempFile("", "tree")
So(err, ShouldBeNil)
err = m.Save(f.Name())
Convey("Fitting should work...", func() {
m := NewMultiLinearSVC("l1", "l2", true, 1.0, 1e-4, nil)
err := m.Fit(X)
So(err, ShouldBeNil)

Convey("Loading should work...", func() {
mLoaded := NewMultiLinearSVC("l1", "l2", true, 1.00, 1e-8, nil)
err := mLoaded.Load(f.Name())
Convey("Predictions should work...", func() {
predictions, err := m.Predict(Y)
So(err, ShouldEqual, nil)
cf, err := evaluation.GetConfusionMatrix(Y, predictions)
So(err, ShouldEqual, nil)
So(evaluation.GetAccuracy(cf), ShouldBeGreaterThan, 0.70)
})

Convey("Saving should work...", func() {
f, err := ioutil.TempFile("", "tree")
So(err, ShouldBeNil)
err = m.Save(f.Name())
So(err, ShouldBeNil)

Convey("Predictions should be the same...", func() {
originalPredictions, err := m.Predict(Y)
So(err, ShouldBeNil)
newPredictions, err := mLoaded.Predict(Y)
Convey("Loading should work...", func() {
mLoaded := NewMultiLinearSVC("l1", "l2", true, 1.00, 1e-8, nil)
err := mLoaded.Load(f.Name())
So(err, ShouldBeNil)
So(base.InstancesAreEqual(originalPredictions, newPredictions), ShouldBeTrue)

Convey("Predictions should be the same...", func() {
originalPredictions, err := m.Predict(Y)
So(err, ShouldBeNil)
newPredictions, err := mLoaded.Predict(Y)
So(err, ShouldBeNil)
So(base.InstancesAreEqual(originalPredictions, newPredictions), ShouldBeTrue)
})

})

})

})

})
}

Expand Down
93 changes: 93 additions & 0 deletions linear_models/integration.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* This file contains functions related to creating + freeing
* objects on behalf of the go runtime
*/

#include "linear.h"
#include <stdlib.h>

extern "C" {

/* NOTE: the Golang versions of the structures must call the corresponding
* Free functions via runtime.SetFinalize */
/* CreateCProblem allocates a new struct problem outside of Golang's
* garbage collection. */
struct problem *CreateCProblem() {
auto ret = new problem();
*ret = {}; // < Clear all fields
return ret;
}

/* CreateCModel allocates a new struct model outside of Golang's
* garbage collection. */
struct model *CreateCModel() {
auto ret = new model();
*ret = {}; // < Clear all fields
return ret;
}

/* CreateCParameter allocates a new struct parameter outside of
* Golang's garbage collection.*/
struct parameter *CreateCParameter() {
return reinterpret_cast<struct parameter*>(calloc(1, sizeof(struct parameter)));
}

/* Free's a previously allocated problem and all its data */
void FreeCProblem(struct problem *p) {
if (p->y != nullptr) {
free(p->y);
p->y = nullptr;
}
if (p->x != nullptr) {
free(p->x);
p->x = nullptr;
}
delete p;
}

/* free's a model with libsvm's internal routines */
void FreeCModel(struct model *m) {
free_model_content(m);
delete m;
}

/* free's a parameter via libsvm */
void FreeCParameter(struct parameter *p) {
if (p == nullptr) {
return;
}
free(p);
}

/* Allocates a vector of doubles for storing target values
* outside of Go's garbage collection */
int AllocateLabelsForProblem (struct problem *p, int numValues) {
p->y = reinterpret_cast<double *>(malloc(sizeof(double) * numValues));
return p->y == nullptr;
}

/* Utility method used to set the target value for a particular
* input row */
void AssignLabelForProblem(struct problem *p, int i, double d) {
p->y[i] = d;
}

/* Allocates a buffer of input rows and inserts the per-row values */
int RiffleFeatures(struct problem *p, int num_offsets, int* row_offsets, struct feature_node *features) {

// Allocate space for the feature node buffer.
p->x = reinterpret_cast<struct feature_node**>(
calloc(num_offsets, sizeof(struct feature_node *))
);
if (p->x == nullptr) {
return -1;
}

for (int i = 0; i < num_offsets; i++) {
int offset = row_offsets[i];
p->x[i] = features + offset;
}
return 0;
}

} /* extern "C" */
19 changes: 19 additions & 0 deletions linear_models/integration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef _H_INTEGRATION_
#define _H_INTEGRATION_

#include "linear.h"

struct problem *CreateCProblem();
void FreeCProblem(struct problem*);
struct model *CreateCModel();
void FreeCModel(struct model*);
struct parameter *CreateCParameter();
void FreeCParameter(struct parameter*);
// Allocates memory outside of golang for describing feature
// vectors.
int RiffleFeatures(struct problem *p, int num_offsets, int* row_offsets, struct feature_node *features);
int AllocateLabelsForProblem(struct problem *, int);
void AssignLabelForProblem(struct problem *, int, double);
struct feature_node *GetFeatureNodeForIndex(struct problem *, int, int);

#endif
Loading