Is it possible to use CURAND together with Thrust inside a device functor? A minimum code example can be:
#include <thrust/device_vector.h>
struct Move
{
Move() {}
using Position = thrust::tuple<double, double>;
__host__ __device__
Position operator()(Position p)
{
thrust::get<0>(p) += 1.0; // use CURAND to add a random N(0,1)
thrust::get<1>(p) += 1.0; // use CURAND to add a random N(0,1)
return p;
}
};
int main()
{
// Create vectors on device
thrust::device_vector<double> p1(10, 0.0);
thrust::device_vector<double> p2(10, 0.0);
// Create zip iterators
auto pBeg = thrust::make_zip_iterator(thrust::make_tuple(p1.begin(), p2.begin()));
auto pEnd = thrust::make_zip_iterator(thrust::make_tuple(p1.end(), p2.end() ));
// Move points in the vectors
thrust::transform(pBeg, pEnd, pBeg, Move());
// Print result (just for debug)
thrust::copy(p1.begin(), p1.end(), std::ostream_iterator<double>(std::cout, "\n"));
thrust::copy(p2.begin(), p2.end(), std::ostream_iterator<double>(std::cout, "\n"));
return 0;
}
What is the right way to create random numbers inside the operator function?
Is it possible to use CURAND together with Thrust inside a device functor?
Yes, it's possible. As indicated by #m.s. most of what you need from curand can be gotten from the curand device api example in the curand documentation. (In fact, there is even a full thrust/curand sample code in the documentation here)
We can mimic the behavior of the setup kernel indicated there with a thrust algorithm call, eg. thrust::for_each_n to setup the initial curand state variables for each device vector element.
After that, it is only necessary to pass the initialized curand state to your Move functor via an additional iterator in your zip iterators, and then call curand_uniform (for example) within the functor.
Here is a fully worked example, based on your code:
$ cat t20.cu
#include <thrust/device_vector.h>
#include <curand_kernel.h>
#include <iostream>
#include <thrust/iterator/counting_iterator.h>
#include <thrust/transform.h>
#include <thrust/for_each.h>
const int seed = 1234;
const int ds = 10;
const int offset = 0;
struct Move
{
Move() {}
using Position = thrust::tuple<double, double, curandState>;
__device__
Position operator()(Position p)
{
curandState s = thrust::get<2>(p);
thrust::get<0>(p) += curand_uniform(&s); // use CURAND to add a random N(0,1)
thrust::get<1>(p) += curand_uniform(&s); // use CURAND to add a random N(0,1)
thrust::get<2>(p) = s;
return p;
}
};
struct curand_setup
{
using init_tuple = thrust::tuple<int, curandState &>;
__device__
void operator()(init_tuple t){
curandState s;
int id = thrust::get<0>(t);
curand_init(seed, id, offset, &s);
thrust::get<1>(t) = s;
}
};
int main()
{
// Create vectors on device
thrust::device_vector<double> p1(ds, 0.0);
thrust::device_vector<double> p2(ds, 0.0);
thrust::device_vector<curandState> s1(ds);
// Create zip iterators
auto pBeg = thrust::make_zip_iterator(thrust::make_tuple(p1.begin(), p2.begin(), s1.begin()));
auto pEnd = thrust::make_zip_iterator(thrust::make_tuple(p1.end(), p2.end(), s1.end() ));
auto pInit = thrust::make_zip_iterator(thrust::make_tuple(thrust::counting_iterator<int>(0), s1.begin()));
// initialize random generator
thrust::for_each_n(pInit, ds, curand_setup());
// Move points in the vectors
thrust::transform(pBeg, pEnd, pBeg, Move());
// Print result (just for debug)
thrust::copy(p1.begin(), p1.end(), std::ostream_iterator<double>(std::cout, "\n"));
thrust::copy(p2.begin(), p2.end(), std::ostream_iterator<double>(std::cout, "\n"));
return 0;
}
$ nvcc -arch=sm_61 -std=c++11 t20.cu -o t20 -lcurand
$ ./t20
0.145468
0.820181
0.550399
0.29483
0.914733
0.868979
0.321921
0.782857
0.0113023
0.28545
0.434899
0.926417
0.811845
0.308556
0.557235
0.501246
0.206681
0.123377
0.539587
0.198575
$
Regarding this question:
What is the right way to create random numbers inside the operator function?
There is no problem with using curand in thrust, but you might also want to be aware that thrust has a built in RNG facility and there is a fully worked usage example here.
Related
Objective
Generate a random spanning tree on a randomly generated graph.
Why: because I don't know yet if I can directly generate random trees with specific number of nodes or leaves in BGL.
My problem
I think it boils down to struggling initializing the Auxillary Property Maps to a sensical default.
As you will see I've tried a number of combination of solutions, in the current state the code fails to compile with a cannot form a reference to 'void'.
What I tried
template<class Graph, class Generator>
auto generate_random_spanning_tree(int n_vertices, int n_edges, Generator& rng)
{
// create empty graph
Graph g;
using vertex_t = typename Graph::vertex_descriptor;
using edge_t = typename Graph::edge_descriptor;
// The key and value types of the map must both be the graph's vertex type.
using predecessor_map_t = boost::static_property_map<vertex_t, vertex_t>;
predecessor_map_t predecessor_map = boost::make_static_property_map<vertex_t,vertex_t>(vertex_t());
// unweighted version selected by passing an object of type static_property_map<double> as the weight map, so let's go
using weight_map_t = boost::static_property_map< double >;
// weight_map_t weight_map = boost::make_transform_value_property_map([](edge_t& e) { return 1.0; }, get(boost::edge_bundle, g)); // nope
//weight_map_t weight_map = boost::make_static_property_map<double>(1.0); // yes but complicated
// weight_map_t weight_map; // nope: why isn't it default constructible?
double my_constant_weight = 1.0;
weight_map_t weight_map(my_constant_weight);
using color_map_t = typename boost::property_map<Graph, boost::vertex_color_t>::type;
color_map_t color_map ; // i suspect this is faulty
// mutate graph
boost::generate_random_graph(g, n_vertices, n_edges, rng);
// pick root, I guess we could as well pick 1st vertex
auto root = boost::random_vertex(g, rng);
boost::random_spanning_tree(g, rng, root, predecessor_map, weight_map, color_map);
return g;
}
First: Your own suspect
using color_map_t = typename boost::property_map<Graph, boost::vertex_color_t>::type;
color_map_t color_map; // i suspect this is faulty
Yes. PropertyMaps map properties. They are like references. Here, color_map
is essentially an unitialized reference. You need something like
color_map_t color_map = get(boost::vertex_color, g);
This, of course, assumes that a vertex_color_t property map has been
associated with the graph by traits. In other words, this assumes that the
property is an iternal property of the graph. Internal properties are often
used by default.
Second: A constant cannot be modified
You use a static property map:
auto predecessor_map =
boost::make_static_property_map<vertex_t, vertex_t>(vertex_t());
That just creates a "virtual" property map (without a backing data structure)
that returns the construction parameter on every key. Logically, the return
value is constant. However, predecessor map is an output parameter:
You will need an LValuePropertyMap there. E.g.
std::map<vertex_t, vertex_t> predecessors;
auto predecessor_map =boost::make_assoc_property_map(predecessors);
Or even
auto vindex = get(boost::vertex_index, g);
auto predecessors = std::vector<vertex_t>(num_vertices(g));
auto predecessor_map = boost::make_safe_iterator_property_map(
predecessors.begin(), predecessors.size(), vindex);
Which uses a vertex index to (optionally) translate descriptors into vector
indices. Note that the second is fixed-size, so initialize it after creating
all vertices.
Other Points Of Interest
// weight_map_t weight_map; // nope: why isn't it default constructible?
What would it do? Surely it won't default to what you think is a good default
(1.0). So I'd just write
auto weight_map = boost::static_property_map(1.0);
Simplified
I'd write the entire function as:
template <class Graph, class Generator>
auto generate_random_spanning_tree(int n_vertices, int n_edges, Generator& rng) {
using vertex_t = typename Graph::vertex_descriptor;
Graph g;
generate_random_graph(g, n_vertices, n_edges, rng);
std::map<vertex_t, vertex_t> predecessors;
random_spanning_tree(g, rng, random_vertex(g, rng),
boost::make_assoc_property_map(predecessors),
boost::static_property_map(1.0), // unweighted
get(boost::vertex_color, g));
return g;
}
Functional Problems
You're asking some good questions yourself. But let me start with some observations.
You have Unspecified/Undefined
Behaviour because your
input graph doesn't conform to the requirements:
There must be a path from every non-root vertex of the graph to the
root; the algorithm typically enters an infinite loop when given a
graph that does not satisfy this property, but may also throw the
exception loop_erased_random_walk_stuck if the search reaches a vertex
with no outgoing edges
Indeed, running your code only completes for a few random seeds, and fails
or runs infinitely for others (this is even increasing the chance of
satisfying the requirements by using undirectedS):
Listing
while true; do (set -x; time ./build/sotest& sleep 3; kill %1); done
You are creating the random spanning tree only to completely forget about
it. Did you intend to return the predecessor map as well (or a derived path
representation)?
Your own questions:
"cannot form a reference to 'void'"
Usually indicates an associated property map could not be found (e.g.
what happens if you fail to supply the vertex_color interior
property. In
this case the remedy is simply to use the default color
map:
random_spanning_tree(
g, rng,
boost::root_vertex(random_vertex(g, rng))
.predecessor_map(boost::make_assoc_property_map(predecessors))
.weight_map(boost::static_property_map(1.0)) // unweighted
);
"I don't know yet if I can directly generate random trees with specific
number of nodes or leaves in BGL."
You can generate random graphs with specific number of nodes and leaves -
as you already demonstrate.
trees.
You can also find random spanning trees (given a graph satisfying the preconditions).
To adhere to the preconditions the simplest way would be to generate
undirected graphs, whilst additionally making sure that the result is
connected. A simple, possible inefficient(?) way to ensure it would be to
explicitly connect components:
if (int n = boost::connected_components(ug, cmap); n > 1) {
std::cout << "Connecting " << n << " components:\n";
for (int c = 1; c < n; ++c)
std::cout << "Added " << add_edge(from(c - 1), from(c), ug).first << "\n";
}
It might be more effective to write your own generating algorithm.
BONUS EXAMPLE
Showing the use of connected_components to make sure the graph is fully
connected, and even building a directed tree from undirected source graph. Also
writing graphviz representations of the "raw" (undirected) source and "tree"
(directed spanning tree), it seems to work pretty well.
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/random.hpp>
#include <boost/graph/random_spanning_tree.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <iomanip>
#include <random>
namespace detail {
template <typename T> struct make_undirected { using type = void; };
template <typename A, typename B, typename C, typename D, typename E, typename F>
struct make_undirected<boost::adjacency_list<A, B, C, D, E, F>> {
using type = boost::adjacency_list<A, B, boost::undirectedS, D, E, F>;
};
} // namespace detail
template <typename T> using Undirect = typename detail::make_undirected<T>::type;
template <class Graph, class Generator>
auto generate_random_spanning_tree(int n_vertices, int n_edges, Generator& rng) {
using UG = Undirect<Graph>;
using vertex_t = typename UG::vertex_descriptor;
// assuming integral vertex index for simplicity
static_assert(std::is_same_v<vertex_t, size_t>);
static_assert(std::is_same_v<typename UG::vertex_descriptor,
typename Graph::vertex_descriptor>);
UG ug;
generate_random_graph(ug, n_vertices, n_edges, rng);
vertex_t const root = random_vertex(ug, rng);
print_graph(ug, std::cout << "Raw root: " << root << ", graph:\n");
{ // make connected
std::map<vertex_t, int> components;
auto from = [&](int component) { // just picking the first...
for (auto& [v, c] : components) if (c == component) return v;
throw std::range_error("component");
};
auto cmap = boost::make_assoc_property_map(components);
if (int n = connected_components(ug, cmap); n > 1) {
std::cout << "Connecting " << n << " components:\n";
for (int c = 1; c < n; ++c)
std::cout << "Added " << add_edge(from(c - 1), from(c), ug).first << "\n";
}
}
std::map<vertex_t, vertex_t> predecessors;
random_spanning_tree(
ug, rng,
boost::root_vertex(root) //
.predecessor_map(boost::make_assoc_property_map(predecessors)));
Graph tree(num_vertices(ug)); // build a tree copy
for (auto v : boost::make_iterator_range(vertices(ug)))
if (predecessors.contains(v))
if (auto pred = predecessors.at(v); ug.null_vertex() != pred)
add_edge(predecessors.at(v), v, tree);
auto save = [&predecessors](auto& g, auto name) {
using edge_t = typename std::decay_t<decltype(g)>::edge_descriptor;
auto tree_edge = [&predecessors](auto s, auto t) {
auto it = predecessors.find(s);
return it != end(predecessors) && it->second == t;
};
boost::dynamic_properties dp;
dp.property("node_id", get(boost::vertex_index, g));
dp.property("color",
boost::make_function_property_map<edge_t>([tree_edge, &g](edge_t e) {
auto s = source(e, g), t = target(e, g);
return tree_edge(s, t) || tree_edge(t, s) ? "red" : "gray";
}));
std::ofstream os(name);
write_graphviz_dp(os, g, dp);
};
save(ug, "raw.dot");
save(tree, "tree.dot");
return std::pair(std::move(tree), root);
}
int main(int argc, char** argv) {
using G = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS>;
auto const seed = argc > 1 ? std::stoull(argv[1]) : std::random_device{}();
std::cout << "seed: " << seed << std::endl;
std::mt19937 prng(seed);
auto [tree, root] = generate_random_spanning_tree<G>(10, 20, prng);
print_graph(tree, std::cout << "From root: " << root << ", tree:\n");
}
Prints the sample seed: 1577455792
Raw root: 7, graph:
0 <--> 7 2 3 7 5 2 8
1 <-->
2 <--> 8 0 4 0 4 9
3 <--> 9 5 0 8
4 <--> 7 7 2 2 5
5 <--> 8 3 0 4
6 <-->
7 <--> 4 4 0 8 0
8 <--> 2 5 7 9 0 3
9 <--> 3 8 2
Connecting 3 components:
Added (0,1)
Added (1,6)
From root: 7, tree:
0 --> 1 3
1 --> 6
2 --> 9
3 --> 5
4 --> 2
5 --> 4 8
6 -->
7 --> 0
8 -->
9 -->
Running locally with:
watch './build/sotest; for a in raw tree; do (set -x ; dot -Tpng -o $a.png $a.dot); done'
Shows random solutions like:
I was practicing vectors and ranges in c++ 20 was stuck at following state.
#include <iostream>
#include <vector>
#include <random>
#include <ranges>
#include <algorithm>
namespace ranges = std::ranges;
struct Model
{
double next_event_time;
};
std::vector<Model> generate_examples(int number)
{
// A uniform random number generator object
// Used as the source of randomness
std::default_random_engine generator;
// Calls () operator on generator to get uniformly-distributed integers
// then transforms the obtained values to output the disired distribution
// Will uniformly generate values between 0 ~ 1
std::uniform_real_distribution<double> distribution(0.0, 1.0);
std::vector<Model> models;
for (auto i = 0; i < number; i++)
{
models.push_back(Model{.next_event_time = distribution(generator)});
}
return models;
}
Model get_next_model(const std::vector<Model> &models)
{
ranges::sort(models | std::views::transform([](const Model &x) { return x.next_event_time; }));
return models[0];
}
int main()
{
std::vector<Model> models = generate_examples(10);
for (const auto &model : models)
std::cout << model.next_event_time << std::endl;
}
I compiled the code with g++ 10.2 and got error
error: no match for call to '(const std::ranges::__sort_fn) ~~~
ranges::sort(models | std::views::transform([](const Model &x) { return x.next_event_time; }));
Instead of std::views::transform, I also tried
lambda expression
ranges::sort(models, {}, &Model::next_event_time)
But they all produced similar no match for call to error. Why is this happening?
Your function should be as this:
Model get_next_model( std::vector<Model> models)
{
ranges::sort(models, ranges::less{}, [](const Model &x) { return x.next_event_time; });
return models[0];
}
There were two problems:
You cannot sort const object (so remove const&)
The signature of sort requires way of sorting (ranges::less) before projections. And transform has no sense here
I am trying to reconstruct two Z bosons.
I am using this tutorial as an example. https://root.cern.ch/doc/master/df103__NanoAODHiggsAnalysis_8C.html
However, I am still not familiar in using ROOT data frame and I am writing it in terms of what I am familiar with.
What I do not understand is how is i1, i2, and idx that is shown in the ROOT tutorial defined?
In my tree, I have the BranchMuon and the variables in this branch are called Muon.PT, Muon.Eta, Muon.Phi and Muon.Charge, and Muon.Mass.
My attempt to follow the tutorial is constructing a muon vector and defining the variables (which generates errors but I am still learning how to work with it):
#ifdef __CLING__
R__LOAD_LIBRARY(libDelphes)
#include "classes/DelphesClasses.h"
#include "external/ExRootAnalysis/ExRootTreeReader.h"
#include "external/ExRootAnalysis/ExRootResult.h"
#else
class ExRootTreeReader;
class ExRootResult;
#endif
#include <vector>
#include "ROOT/RDataFrame.hxx"
#include "ROOT/RVec.hxx"
#include "ROOT/RDF/RInterface.hxx"
#include "TCanvas.h"
#include "TH1D.h"
#include "TLatex.h"
#include "TLegend.h"
#include "Math/Vector4Dfwd.h"
#include "TStyle.h"
using namespace ROOT::VecOps;
using RNode = ROOT::RDF::RNode;
using rvec_f = const RVec<float> &;
using rvec_i = const RVec<int> &;
const auto z_mass = 91.2;
template<typename T>
void CollectionFilter(const TClonesArray& inColl ,vector<T*>& outColl, Double_t ptMin=30, Double_t etaMax=2.5)
{
const TObject *object;
for (Int_t i = 0; i < inColl.GetEntriesFast(); i++)
{
object = inColl.At(i);
const T *t = static_cast<const T*>(object);
if(t->P4().Pt() < ptMin) continue;
if(TMath::Abs(t->P4().Eta()) > etaMax) continue;
outColl.push_back(t);
}
}
void selectMuon(const char *inputFile)
{
gSystem->Load("libDelphes");
// Create chain of root trees
TChain chain("Delphes");
chain.Add(inputFile);
// Create object of class ExRootTreeReader
ExRootTreeReader *treeReader = new ExRootTreeReader(&chain);
Long64_t numberOfEntries = treeReader->GetEntries();
// Get pointers to branches used in this analysis
TClonesArray *branchMuon = treeReader->UseBranch("Muon");
// Book histograms
TH1F *histZMass = new TH1F("mass", "M_{inv}(Z[1]); M_inv (GeV/c^2); Events", 50, 0.0, 1500);
TH1F *histDiMuonMass = new TH1F("mass", "M_{inv}(Z[3]Z[5]); M_inv (GeV/c^2); Events", 50, 0.0, 1500);
// Define variables
float_t PT, Eta, Phi, Mass, Charge;
// Initializing the vectors
//vector<const Muon*> *muons = new vector<const Muon*>();;
//MuonVector muon(PT, Eta, Phi, Mass, Charge);
RVec<RVec<size_t>> reco_zz_to_4l(rvec_f PT, rvec_f Eta, rvec_f Phi, rvec_f Mass, rvec_i Charge);
RVec<RVec<size_t>> idx(2);
idx[0].reserve(2); idx[1].reserve(2);
auto idx_cmb = Combinations(PT, 2);
for (size_t i = 0; i < idx_cmb[0].size(); i++)
{
const auto i1 = idx_cmb[0][i];
const auto i2 = idx_cmb[1][i];
if(Charge[i1] != Charge[i2]
{
ROOT::Math::PtEtaPhiMVector m1(PT[0], Eta[0], Phi[0], Mass[0]);
ROOT::Math::PtEtaPhiMVector m2(PT[1], Eta[1], Phi[1], Mass[1]);
const auto mass = (m1 + m2).M();
}
}
histDiMuonMass->Fill(mass);
// end of event for loop
histDiMuonMass->Draw();
}
The errors that I am getting is below.
In file included from input_line_188:1:
/mnt/c/1/MG5_aMC_v2_6_6/Delphes/examples/selectMuon.C:89:16: error: subscripted value is not an array, pointer, or vector
if(Charge[i1] != Charge[i2]
~~~~~~^~~
/mnt/c/1/MG5_aMC_v2_6_6/Delphes/examples/selectMuon.C:89:30: error: subscripted value is not an array, pointer, or vector
if(Charge[i1] != Charge[i2]
~~~~~~^~~
/mnt/c/1/MG5_aMC_v2_6_6/Delphes/examples/selectMuon.C:100:28: error: use of undeclared identifier 'mass'
histDiMuonMass->Fill(mass);
How can I fix it?
Thank you.
I learnt that I had a previous definition of charge to be a float while I am using it to be an array.
Some background:
I wrote a single layer multi output perceptron class in C++. It uses the typical WX + b discriminant function and allows for user-defined activation functions. I have tested everything pretty throughly and it all seems to be working as I expect it to. I noticed a small logical error in my code, and when I attempted to fix it the network performed much worse than before. The error is as follows:
I evaluate the value at each output neuron using the following code:
output[i] =
activate_(std::inner_product(weights_[i].begin(), weights_[i].end(),
features.begin(), -1 * biases_[i]));
Here I treat the bias input as a fixed -1, but when I apply the learning rule to each bias, I treat the input as +1.
// Bias can be treated as a weight with a constant feature value of 1.
biases_[i] = weight_update(1, error, learning_rate_, biases_[i]);
So I attempted to fix my mistake by changing the call to weight_updated to be conistent with the output evaluation:
biases_[i] = weight_update(-1, error, learning_rate_, biases_[i]);
But doing so results in a 20% drop in accuracy!
I have been pulling my hair out for the past few days trying to find some other logical error in my code which might explain this strange behaviour, but have come up empty handed. Can anyone with more knowledge than I provide any insight into this? I have provided the entire class below for reference. Thank you in advance.
#ifndef SINGLE_LAYER_PERCEPTRON_H
#define SINGLE_LAYER_PERCEPTRON_H
#include <cassert>
#include <functional>
#include <numeric>
#include <vector>
#include "functional.h"
#include "random.h"
namespace qp {
namespace rf {
namespace {
template <typename Feature>
double weight_update(const Feature& feature, const double error,
const double learning_rate, const double current_weight) {
return current_weight + (learning_rate * error * feature);
}
template <typename T>
using Matrix = std::vector<std::vector<T>>;
} // namespace
template <typename Feature, typename Label, typename ActivationFn>
class SingleLayerPerceptron {
public:
// For testing only.
SingleLayerPerceptron(const Matrix<double>& weights,
const std::vector<double>& biases, double learning_rate)
: weights_(weights),
biases_(biases),
n_inputs_(weights.front().size()),
n_outputs_(biases.size()),
learning_rate_(learning_rate) {}
// Initialize the layer with random weights and biases in [-1, 1].
SingleLayerPerceptron(std::size_t n_inputs, std::size_t n_outputs,
double learning_rate)
: n_inputs_(n_inputs),
n_outputs_(n_outputs),
learning_rate_(learning_rate) {
weights_.resize(n_outputs_);
std::for_each(
weights_.begin(), weights_.end(), [this](std::vector<double>& wv) {
generate_back_n(wv, n_inputs_,
std::bind(random_real_range<double>, -1, 1));
});
generate_back_n(biases_, n_outputs_,
std::bind(random_real_range<double>, -1, 1));
}
std::vector<double> predict(const std::vector<Feature>& features) const {
std::vector<double> output(n_outputs_);
for (auto i = 0ul; i < n_outputs_; ++i) {
output[i] =
activate_(std::inner_product(weights_[i].begin(), weights_[i].end(),
features.begin(), -1 * biases_[i]));
}
return output;
}
void learn(const std::vector<Feature>& features,
const std::vector<double>& true_output) {
const auto actual_output = predict(features);
for (auto i = 0ul; i < n_outputs_; ++i) {
const auto error = true_output[i] - actual_output[i];
for (auto weight = 0ul; weight < n_inputs_; ++weight) {
weights_[i][weight] = weight_update(
features[weight], error, learning_rate_, weights_[i][weight]);
}
// Bias can be treated as a weight with a constant feature value of 1.
biases_[i] = weight_update(1, error, learning_rate_, biases_[i]);
}
}
private:
Matrix<double> weights_; // n_outputs x n_inputs
std::vector<double> biases_; // 1 x n_outputs
std::size_t n_inputs_;
std::size_t n_outputs_;
ActivationFn activate_;
double learning_rate_;
};
struct StepActivation {
double operator()(const double x) const { return x > 0 ? 1 : -1; }
};
} // namespace rf
} // namespace qp
#endif /* SINGLE_LAYER_PERCEPTRON_H */
I ended up figuring it out...
My fix was indeed correct and the loss of accuracy was just a consequence of having a lucky (or unlucky) dataset.
It is probably something basic because I am just starting to learn LLVM..
The following creates a factorial function and tries to git and execute it (I know the generated func is correct because I was able to static compile and execute it).
But I get segmentation fault upon execution of the function (in EE->runFunction(TheF, Args))
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/PassManager.h"
#include "llvm/CallingConv.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/Assembly/PrintModulePass.h"
#include "llvm/Support/IRBuilder.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/ExecutionEngine/GenericValue.h"
using namespace llvm;
Module* makeLLVMModule() {
// Module Construction
LLVMContext& ctx = getGlobalContext();
Module* mod = new Module("test", ctx);
Constant* c = mod->getOrInsertFunction("fact64",
/*ret type*/ IntegerType::get(ctx,64),
IntegerType::get(ctx,64),
/*varargs terminated with null*/ NULL);
Function* fact64 = cast<Function>(c);
fact64->setCallingConv(CallingConv::C);
/* Arg names */
Function::arg_iterator args = fact64->arg_begin();
Value* x = args++;
x->setName("x");
/* Body */
BasicBlock* block = BasicBlock::Create(ctx, "entry", fact64);
BasicBlock* xLessThan2Block= BasicBlock::Create(ctx, "xlst2_block", fact64);
BasicBlock* elseBlock = BasicBlock::Create(ctx, "else_block", fact64);
IRBuilder<> builder(block);
Value *One = ConstantInt::get(Type::getInt64Ty(ctx), 1);
Value *Two = ConstantInt::get(Type::getInt64Ty(ctx), 2);
Value* xLessThan2 = builder.CreateICmpULT(x, Two, "tmp");
//builder.CreateCondBr(xLessThan2, xLessThan2Block, cond_false_2);
builder.CreateCondBr(xLessThan2, xLessThan2Block, elseBlock);
/* Recursion */
builder.SetInsertPoint(elseBlock);
Value* xMinus1 = builder.CreateSub(x, One, "tmp");
std::vector<Value*> args1;
args1.push_back(xMinus1);
Value* recur_1 = builder.CreateCall(fact64, args1.begin(), args1.end(), "tmp");
Value* retVal = builder.CreateBinOp(Instruction::Mul, x, recur_1, "tmp");
builder.CreateRet(retVal);
/* x<2 */
builder.SetInsertPoint(xLessThan2Block);
builder.CreateRet(One);
return mod;
}
int main(int argc, char**argv) {
long long x;
if(argc > 1)
x = atol(argv[1]);
else
x = 4;
Module* Mod = makeLLVMModule();
verifyModule(*Mod, PrintMessageAction);
PassManager PM;
PM.add(createPrintModulePass(&outs()));
PM.run(*Mod);
// Now we going to create JIT
ExecutionEngine *EE = EngineBuilder(Mod).create();
// Call the function with argument x:
std::vector<GenericValue> Args(1);
Args[0].IntVal = APInt(64, x);
Function* TheF = cast<Function>(Mod->getFunction("fact64")) ;
/* The following CRASHES.. */
GenericValue GV = EE->runFunction(TheF, Args);
outs() << "Result: " << GV.IntVal << "\n";
delete Mod;
return 0;
}
Edit:
The correct way to enable JIT (see the accepted answer below):
1.#include "llvm/ExecutionEngine/Jit.h"`
2.InitializeNativeTarget();
I would bet that the ExecutionEngine pointer is null.... You are missing a call to InitializeNativeTarget, the documentation says:
InitializeNativeTarget - The main program should call this function to initialize the native target corresponding to the host. This is useful for JIT applications to ensure that the target gets linked in correctly.
Since there is no JIT compiler available without calling InitializeNativeTarget, ModuleBuilder selects the interpreter (if available). Probably not what you wanted. You may want to look at my previous post on this subject.
#include "llvm/ExecutionEngine/Interpreter.h"
Including that header (llvm/ExecutionEngine/Interpreter.h) forces a static initialisation of the JIT. Not the best design decision, but at least it works.