How to pass a class method? - c++

I am trying to use this library for some work. In the example, which is given on their website, they use an operator for defining gradient calculation. I want to use a method,i.e., getGradient, instead of an operator. I have tried several ways, including std::bind(), &Rosenbrock::getGradient. None of them works fine. Any idea how this can be done? I don't need a full answer, just hint will be enough.
#include <Eigen/Core>
#include <iostream>
#include <LBFGS.h>
using Eigen::VectorXd;
using namespace LBFGSpp;
class Rosenbrock
{
private:
int n;
public:
Rosenbrock(int n_) : n(n_) {}
double operator()(const VectorXd& x, VectorXd& grad);
double getGradient(const VectorXd& x, VectorXd& grad);
};
double Rosenbrock::operator()(const VectorXd& x, VectorXd& grad){
double fx = 0.0;
for(int i = 0; i < n; i += 2)
{
double t1 = 1.0 - x[i];
double t2 = 10 * (x[i + 1] - x[i] * x[i]);
grad[i + 1] = 20 * t2;
grad[i] = -2.0 * (x[i] * grad[i + 1] + t1);
fx += t1 * t1 + t2 * t2;
}
return fx;
}
double Rosenbrock::getGradient(const VectorXd& x, VectorXd& grad){
double fx = 0.0;
for(int i = 0; i < n; i += 2)
{
double t1 = 1.0 - x[i];
double t2 = 10 * (x[i + 1] - x[i] * x[i]);
grad[i + 1] = 20 * t2;
grad[i] = -2.0 * (x[i] * grad[i + 1] + t1);
fx += t1 * t1 + t2 * t2;
}
return fx;
}
int main(int argc, char** argv){
const int n = 10;
// Set up parameters
LBFGSParam<double> param;
param.epsilon = 1e-6;
param.max_iterations = 100;
// Create solver and function object
LBFGSSolver<double> solver(param);
Rosenbrock fun(n);
// Initial guess
VectorXd x = VectorXd::Zero(n);
double fx;
//int niter = solver.minimize(fun, x, fx);
int niter = solver.minimize(std::bind(Rosenbrock::getGradient, fun, _1, _2), x, fx);
// I want to do something similar to this
std::cout << niter << " iterations" << std::endl;
std::cout << "x = \n" << x.transpose() << std::endl;
std::cout << "f(x) = " << fx << std::endl;
return 0;
}

What about:
struct Bind
{
Rosenbrock & impl;
template <typename X, typename Y> // Template here because I'm lazy writing the full type
double operator () (X x, Y y) { return impl.getGradient(x, y); }
Bind(Rosenbrock & impl) : impl(impl) {}
};
// Then use Bind with your solver:
Bind b(fun);
int niter = solver.minimize(b);
// Example with a template (replace X, Y by the argument signature of the method you are binding)
template <typename T, double (T::*Func)(X, Y)>
struct Bind
{
T & impl;
double operator()(X x, Y y) { return (impl.*Func)(x, y); }
Bind(T & ref) : impl(ref) {}
};
// Using like
Bind<Rosenbrock, &Rosenbrock::getGradient> b(fun);
The above Bind class can be a template. It can be a lambda. You're just redirecting the operator() to the method in the binder's operator ().

Related

What can cause LNK2019 error in VisualStudio?

I have created a simple cpp project in Visual Studio, but have encountered the follwoing linking error:
LNK2019: unresolved external symbol "public: class std::vector > __thiscall solver::euler(class point_3d (__cdecl*)(class point_3d,double),double,int,class point_3d,double)" (?euler#?$solver#Vpoint_3d####QAE?AV?$vector#Vpoint_3d##V?$allocator#Vpoint_3d###std###std##P6A?AVpoint_3d##V4#N#ZNH0N#Z
I have no idea what may be causing this and would be gratefull for any advice.
Files that constitute the project:
main.cpp
#include "point_3d.h"
#include "solver.h"
point_3d lorentz(point_3d val, double t) {
double x = 10*(val.get_y()-val.get_x());
double y = val.get_x()*(28.0- val.get_z())- val.get_y();
double z = val.get_x()* val.get_y()-(8.0/3)* val.get_z();
return point_3d(x, y, z);
}
int main(){
auto file = std::ofstream("data/results.json");
solver<point_3d> sol;
std::vector<point_3d> result = sol.euler(lorentz, 0.01, 50000, point_3d(1, 1, 1));
}
solver.h
template<typename T>
class solver{
public:
std::vector<T> euler(T (*f)(T val, double s),double h, int steps, T val, double t0 = 0.0);
std::vector<T> back_euler(T (*f)(T val, double s), double h, int steps, T val, double t0 = 0.0,int n=50);
std::vector<T> rk_2(T (*f)(T val, double s), double h, int steps, T val, double t0 = 0.0);
std::vector<T> rk_4(T (*f)(T val, double s), double h, int steps, T val, double t0 = 0.0);
};
solver.cpp
#include "solver.h"
template<typename T>
std::vector<T> solver<T>::euler(T (*f)(T val, double s), double h, int steps, T val, double t0 ) {
std::vector<T>res;
res.resize(steps + 1);
res[0] = val;
double t = t0;
for (int i = 0; i < steps;i++) {
res[i + 1] = res[i] + h * f(res[i], t);
t += h;
}
return res;
}
template<typename T>
std::vector<T> solver<T>::back_euler(T (*f)(T val, double s), double h, int steps, T val, double t0, int n) {
std::vector<T>res;
res.resize(steps + 1);
res[0] = val;
double t = t0;
for (int i = 0; i < steps; i++) {
t += h;
res[i + 1] = res[i];
for (int j = 0; j < n; j++) {
res[i + 1] += res[i] + h * f(res[i + 1],t);
}
}
return res;
}
template<typename T>
std::vector<T> solver<T>::rk_2(T (*f)(T val, double s), double h, int steps, T val, double t0) {
std::vector<T>res;
res.resize(steps + 1);
res[0] = val;
double t = t0;
for (int i = 0; i < steps; i++) {
auto k = h * f(res[i], t) / 2.0;
res[i + 1] = res[i] + h * f(k+res[i],t+h/2.0);
t += h;
}
return res;
}
template<typename T>
std::vector<T> solver<T>::rk_4(T (*f)(T val, double s), double h, int steps, T val, double t0) {
std::vector<T>res;
res.resize(steps + 1);
res[0] = val;
double t = t0;
for (int i = 0; i < steps; i++) {
auto k1 = f(res[i],t);
auto k2 = f(res[i]+h/2.0*k1, t+h/3.0);
auto k3 = f(res[i] + h / 2.0 * k2, t + 2*h / 3.0);
auto k4 = f(res[i] + h * k3, t + h);
res[i + 1] = res[i] + (k1 / 6 + k2 / 3 + k3 / 3 + k4 / 6) * h;
t += h;
}
return res;
}
point_3d.h
class point_3d{
double x;
double y;
double z;
public:
point_3d(double x, double y, double z): x(x), y(y), z(z) {};
double get_x();
double get_y();
double get_z();
point_3d operator+(point_3d other);
point_3d& operator=(point_3d other);
friend point_3d operator*(double k, point_3d p);
};
point_3d.cpp
#include "point_3d.h"
double point_3d::get_x() {
return x;
}
double point_3d::get_y() {
return y;
}
double point_3d::get_z() {
return z;
}
point_3d point_3d::operator+(point_3d other) {
double x = get_x() + other.get_x();
double y = get_y() + other.get_y();
double z = get_z() + other.get_z();
return point_3d(x, y, z);
}
point_3d& point_3d::operator=(point_3d other) {
this->x = other.get_x();
this->y = other.get_y();
this->z = other.get_z();
return *this;
}
point_3d operator*(double k, point_3d p) {
double x = p.get_x();
double y = p.get_y();
double z = p.get_z();
return point_3d(k * x, k * y, k * z);
}
You need to include vector library to use std::vector.
#include <vector>
"Unresolved symbol" means that there is a class or a function declared but not found anywhere in the project (or in the libraries it includes).

C++: std::vector of function template pointers

The idea is to call similar Runge-Kutta function templates in a loop instead of doing it one by one. I've looked at similar solutions, I also tried void*, but was not able to apply to my problem due to conversion errors.
EDIT: these function templates are supposed to be used with fixed types, it's an overkill, but I would like to see whether there is an elegant solution.
Here is the full code:
#include <iostream>
#include <iomanip>
#include <cmath>
#include <functional>
template <typename X, typename F, typename H = double>
X runge_kutta1(const X &x, const F &f, H h)
{
return x + h * f(x);
}
template <typename X, typename F, typename H = double>
X runge_kutta2(const X &x, const F &f, H h)
{
X k1 = f(x);
return x + 0.5 * h * (k1 + f(x + h * k1));
}
struct pair
{
double v;
double w;
pair(double v, double w)
: v{v}, w{w}
{
}
};
inline pair operator*(double h, pair p)
{
return {h * p.v, h * p.w};
}
inline pair operator+(pair p1, pair p2)
{
return {p1.v + p2.v, p1.w + p2.w};
}
inline std::ostream &operator<<(std::ostream &stream, const pair &p)
{
stream << p.v << ", " << p.w;
return stream;
}
int main() {
{
double x = 0.0;
double x1 = 1.0;
double lambda = 2;
double h = 1.0E-3;
pair p{1.0 / lambda, 0.0};
const std::function<pair(pair)> cat =
[&lambda](const pair &p) { return pair{p.w, lambda * sqrt(1.0 + p.w * p.w)}; };
while (x + h < x1)
{
p = runge_kutta1(p, cat, h);
x = x + h;
}
p = runge_kutta1(p, cat, x1 - x);
pair expected = {cosh(lambda * x1) / lambda, sinh(lambda * x1)};
pair error = p + -1.0 * expected;
std::cout << std::setprecision(18) << "runge_kutta1:\nFinal result: " << p << "\n";
std::cout << "Error: " << error << "\n\n";
}
{
double x = 0.0;
double x1 = 1.0;
double lambda = 2;
double h = 1.0E-3;
pair p{1.0 / lambda, 0.0};
const std::function<pair(pair)> cat =
[&lambda](const pair &p) { return pair{p.w, lambda * sqrt(1.0 + p.w * p.w)}; };
while (x + h < x1)
{
p = runge_kutta2(p, cat, h);
x = x + h;
}
p = runge_kutta2(p, cat, x1 - x);
pair expected = {cosh(lambda * x1) / lambda, sinh(lambda * x1)};
pair error = p + -1.0 * expected;
std::cout << "runge_kutta2:\nFinal result: " << p << "\n";
std::cout << "Error: " << error << "\n";
}
}
What I would like to have (the actual algorithm is simplified for the sake of readability):
std::vector<?> functions{runge_kutta1, runge_kutta2}; // two just as an example
double p = 1.0;
double h = 1.0E-3;
double lambda = 2;
const std::function<pair(pair)> cat =
[&lambda](const pair &p) { return pair{p.w, lambda * sqrt(1.0 + p.w * p.w)}; };
for (const auto& func : functions) {
double t = func(p, cat, h);
std::cout << t << "\n";
}
You can not have a pointer to a function template. You can only have a pointer to specific instantiation of the template. In a same manner, you can't pack a template into std::function - only a specific instantiation of it.
And you can only put objects of the same type in the container - so your pointers will have to be of the same type (i.e. the function they point to should accept the same type of arguments and return the same type).
std::function will have the same limitation - all std::functions inside the container must be of the same type, in terms of return value and arguments.

2D Poisson-disk sampling in a specific square (not a unit square) with specific minimum distance

Is there any way I can modify the poisson-disk points generator finding here.I need to generate new poisson points using the coordinates of points in the textfile.txt to improve the distribution. below the c++ code of poisson-disk sampling in a unit square.
poissonGenerator.h:
#include <vector>
#include <random>
#include <stdint.h>
#include <time.h>
namespace PoissoGenerator
{
class DefaultPRNG
{
public:
DefaultPRNG()
: m_Gen(std::random_device()())
, m_Dis(0.0f, 1.f)
{
// prepare PRNG
m_Gen.seed(time(nullptr));
}
explicit DefaultPRNG(unsigned short seed)
: m_Gen(seed)
, m_Dis(0.0f, 1.f)
{
}
double RandomDouble()
{
return static_cast <double>(m_Dis(m_Gen));
}
int RandomInt(int Max)
{
std::uniform_int_distribution<> DisInt(0, Max);
return DisInt(m_Gen);
}
private:
std::mt19937 m_Gen;
std::uniform_real_distribution<double> m_Dis;
};
struct sPoint
{
sPoint()
: x(0)
, y(0)
, m_valid(false){}
sPoint(double X, double Y)
: x(X)
, y(Y)
, m_valid(true){}
double x;
double y;
bool m_valid;
//
bool IsInRectangle() const
{
return x >= 0 && y >= 0 && x <= 1 && y <= 1;
}
//
bool IsInCircle() const
{
double fx = x - 0.5f;
double fy = y - 0.5f;
return (fx*fx + fy*fy) <= 0.25f;
}
};
struct sGridPoint
{
sGridPoint(int X, int Y)
: x(X)
, y(Y)
{}
int x;
int y;
};
double GetDistance(const sPoint& P1, const sPoint& P2)
{
return sqrt((P1.x - P2.x)*(P1.x - P2.x) + (P1.y - P2.y)*(P1.y - P2.y));
}
sGridPoint ImageToGrid(const sPoint& P, double CellSize)
{
return sGridPoint((int)(P.x / CellSize), (int)(P.y / CellSize));
}
struct sGrid
{
sGrid(int W, int H, double CellSize)
: m_W(W)
, m_H(H)
, m_CellSize(CellSize)
{
m_Grid.resize((m_H));
for (auto i = m_Grid.begin(); i != m_Grid.end(); i++){ i->resize(m_W); }
}
void Insert(const sPoint& P)
{
sGridPoint G = ImageToGrid(P, m_CellSize);
m_Grid[G.x][G.y] = P;
}
bool IsInNeighbourhood(sPoint Point, double MinDist, double CellSize)
{
sGridPoint G = ImageToGrid(Point, CellSize);
//number of adjacent cell to look for neighbour points
const int D = 5;
// Scan the neighbourhood of the Point in the grid
for (int i = G.x - D; i < G.x + D; i++)
{
for (int j = G.y - D; j < G.y + D; j++)
{
if (i >= 0 && i < m_W && j >= 0 && j < m_H)
{
sPoint P = m_Grid[i][j];
if (P.m_valid && GetDistance(P, Point) < MinDist){ return true; }
}
}
}
return false;
}
private:
int m_H;
int m_W;
double m_CellSize;
std::vector< std::vector< sPoint> > m_Grid;
};
template <typename PRNG>
sPoint PopRandom(std::vector<sPoint>& Points, PRNG& Generator)
{
const int Idx = Generator.RandomInt(Points.size() - 1);
const sPoint P = Points[Idx];
Points.erase(Points.begin() + Idx);
return P;
}
template <typename PRNG>
sPoint GenerateRandomPointAround(const sPoint& P, double MinDist, PRNG& Generator)
{
// Start with non-uniform distribution
double R1 = Generator.RandomDouble();
double R2 = Generator.RandomDouble();
// radius should be between MinDist and 2 * MinDist
double Radius = MinDist * (R1 + 1.0f);
//random angle
double Angle = 2 * 3.141592653589f * R2;
// the new point is generated around the point (x, y)
double X = P.x + Radius * cos(Angle);
double Y = P.y + Radius * sin(Angle);
return sPoint(X, Y);
}
// Return a vector of generated points
// NewPointsCount - refer to bridson-siggraph07-poissondisk.pdf
// for details (the value 'k')
// Circle - 'true' to fill a circle, 'false' to fill a rectangle
// MinDist - minimal distance estimator, use negative value for default
template <typename PRNG = DefaultPRNG>
std::vector<sPoint> GeneratePoissonPoints(rsize_t NumPoints, PRNG& Generator, int NewPointsCount = 30,
bool Circle = true, double MinDist = -1.0f)
{
if (MinDist < 0.0f)
{
MinDist = sqrt(double(NumPoints)) / double(NumPoints);
}
std::vector <sPoint> SamplePoints;
std::vector <sPoint> ProcessList;
// create the grid
double CellSize = MinDist / sqrt(2.0f);
int GridW = (int)(ceil)(1.0f / CellSize);
int GridH = (int)(ceil)(1.0f / CellSize);
sGrid Grid(GridW, GridH, CellSize);
sPoint FirstPoint;
do
{
FirstPoint = sPoint(Generator.RandomDouble(), Generator.RandomDouble());
} while (!(Circle ? FirstPoint.IsInCircle() : FirstPoint.IsInRectangle()));
//Update containers
ProcessList.push_back(FirstPoint);
SamplePoints.push_back(FirstPoint);
Grid.Insert(FirstPoint);
// generate new points for each point in the queue
while (!ProcessList.empty() && SamplePoints.size() < NumPoints)
{
#if POISSON_PROGRESS_INDICATOR
// a progress indicator, kind of
if (SamplePoints.size() % 100 == 0) std::cout << ".";
#endif // POISSON_PROGRESS_INDICATOR
sPoint Point = PopRandom<PRNG>(ProcessList, Generator);
for (int i = 0; i < NewPointsCount; i++)
{
sPoint NewPoint = GenerateRandomPointAround(Point, MinDist, Generator);
bool Fits = Circle ? NewPoint.IsInCircle() : NewPoint.IsInRectangle();
if (Fits && !Grid.IsInNeighbourhood(NewPoint, MinDist, CellSize))
{
ProcessList.push_back(NewPoint);
SamplePoints.push_back(NewPoint);
Grid.Insert(NewPoint);
continue;
}
}
}
#if POISSON_PROGRESS_INDICATOR
std::cout << std::endl << std::endl;
#endif // POISSON_PROGRESS_INDICATOR
return SamplePoints;
}
}
and the main program is:
poisson.cpp
#include "stdafx.h"
#include <vector>
#include <iostream>
#include <fstream>
#include <memory.h>
#define POISSON_PROGRESS_INDICATOR 1
#include "PoissonGenerator.h"
const int NumPoints = 20000; // minimal number of points to generate
int main()
{
PoissonGenerator::DefaultPRNG PRNG;
const auto Points =
PoissonGenerator::GeneratePoissonPoints(NumPoints,PRNG);
std::ofstream File("Poisson.txt", std::ios::out);
File << "NumPoints = " << Points.size() << std::endl;
for (const auto& p : Points)
{
File << " " << p.x << " " << p.y << std::endl;
}
system("PAUSE");
return 0;
}
Suppose you have a point in the space [0,1] x [0,1], in the form of a std::pair<double, double>, but desire points in the space [x,y] x [w,z].
The function object
struct ProjectTo {
double x, y, w, z;
std::pair<double, double> operator(std::pair<double, double> in)
{
return std::make_pair(in.first * (y - x) + x, in.second * (z - w) + w);
}
};
will transform such an input point into the desired output point.
Suppose further you have a std::vector<std::pair<double, double>> points, all drawn from the input distribution.
std::copy(points.begin(), points.end(), points.begin(), ProjectTo{ x, y, w, z });
Now you have a vector of points in the output space.

C++ Compiler error: C3867 when using a function as member of a class

When I compile the following:
#include "stdafx.h"
#define _USE_MATH_DEFINES
#include <cmath>
#include <iostream>
using namespace std;
#define A 10
double psi(double x);
double qgaus(double(*func)(double), double a, double b);
double normalize();
double psi(double x) {
return pow(sin(M_PI_2*x/ A), 2);
}
double qgaus(double(*func)(double), double a, double b) {
double xr, xm, dx, s;
static double x[] = { 0.0, 0.1488743389, 0.4333953941, 0.6794095682,0.8650633666,0.9739065285 };
static double w[] = { 0.0, 0.2955242247, 0.2692667193, 0.2190863625,0.1494513491,0.0666713443 };
xm = 0.5*(b + a);
xr = 0.5*(b - a);
s = 0;
for (int j = 1; j <= 5; j++) {
dx = xr*x[j];
s += w[j] * ((*func)(xm + dx) + (*func)(xm - dx));
}
return s *= xr;
}
double normalize() {
double N;
N = 1 / sqrt(qgaus(psi, 0.0, A));
return N;
}
int main()
{
double Norm = normalize();
cout << Norm << endl;
return 0;
}
This code compiles without an error. However when I try to put two of the routines into a class as shown here with the changes.
class PsiC {
public:
double psi(double x);
double normalize();
};
double PsiC::normalize() {
PsiC my_psi;
double N;
N = 1 / sqrt(qgaus(my_psi.psi, 0.0, A));
return N;
}
Now using the following in main:
PsiC my_psi;
double Norm = my_psi.normalize();
cout << Norm << endl;
The statement N = 1 / sqrt(qgaus(my_psi.psi, 0.0, A)); gives the compiler error:
'func': function call missing argument list; use '&func' to create a pointer to member.
Note: I only have two member in this class now; however, I intend to add more member later.

Newton Fractal generation

I wanted to write my own newton fractal generator.. It's using OpenCL... but that's not the problem.. my problem is that atm. only veerryy few pixels are converging.
So to explain what I've done so far:
I've selected a function I wanted to use: f(z)=z^4-1 (for testing purposes)
I've calculated the roots of this function: 1+0î, -1+0î, 0+1î, 0-1î
I've written a OpenCL Host and Kernel:
the kernel uses a struct with 4 doubles: re (real), im (imaginary), r (as abs), phi (as argument, polar angle or how you call it)
computes from resolution, zoom and global_work_id the "type" of the pixel and the intensity - where type is the root the newton method is converging to / whether it's diverging
Here's what I get rendered:
Here is the whole kernel:
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
#define pi 3.14159265359
struct complex {
double im;
double re;
double r;
double phi;
};
struct complex createComplexFromPolar(double _r, double _phi){
struct complex t;
t.r = _r;
t.phi = _phi;
t.re = cos(t.phi)*t.r;
t.im = sin(t.phi)*t.r;
return t;
}
struct complex createComplexFromKarthes(double real, double imag){
struct complex t;
t.re = real;
t.im = imag;
t.phi = atan(imag / real);
t.r = sqrt(t.re*t.re + t.im*t.im);
return t;
}
struct complex recreateComplexFromKarthes(struct complex t){
return t = createComplexFromKarthes(t.re, t.im);
}
struct complex recreateComplexFromPolar(struct complex t){
return t = createComplexFromPolar(t.r, t.phi);
}
struct complex addComplex(const struct complex z, const struct complex c){
struct complex t;
t.re = c.re + z.re;
t.im = c.im + z.im;
return recreateComplexFromKarthes(t);
}
struct complex subComplex(const struct complex z, const struct complex c){
struct complex t;
t.re = z.re - c.re;
t.im = z.im - c.im;
return recreateComplexFromKarthes(t);
}
struct complex addComplexScalar(const struct complex z, const double n){
struct complex t;
t.re = z.re + n;
return recreateComplexFromKarthes(t);
}
struct complex subComplexScalar(const struct complex z, const double n){
struct complex t;
t.re = z.re - n;
return recreateComplexFromKarthes(t);
}
struct complex multComplexScalar(const struct complex z, const double n) {
struct complex t;
t.re = z.re * n;
t.im = z.im * n;
return recreateComplexFromKarthes(t);
}
struct complex multComplex(const struct complex z, const struct complex c) {
return createComplexFromPolar(z.r*c.r, z.phi + c.phi);
}
struct complex powComplex(const struct complex z, int i){
struct complex t = z;
for (int j = 0; j < i; j++){
t = multComplex(t, z);
}
return t;
}
struct complex divComplex(const struct complex z, const struct complex c) {
return createComplexFromPolar(z.r / c.r, z.phi - c.phi);
}
bool compComplex(const struct complex z, const struct complex c, float comp){
struct complex t = subComplex(z, c);
if (fabs(t.re) <= comp && fabs(t.im) <= comp)
return true;
return false;
}
__kernel void newtonFraktal(__global const int* res, __global const int* zoom, __global int* offset, __global const double* param, __global int* result, __global int* resType){
const int x = get_global_id(0) + offset[0];
const int y = get_global_id(1) + offset[1];
const int xRes = res[0];
const int yRes = res[1];
const double a = (x - (xRes / 2)) == 0 ? 0 : (double)(zoom[0] / (x - (double)(xRes / 2)));
const double b = (y - (yRes / 2)) == 0 ? 0 : (double)(zoom[1] / (y - (double)(yRes / 2)));
struct complex z = createComplexFromKarthes(a, b);
struct complex zo = z;
struct complex c = createComplexFromKarthes(param[0], param[1]);
struct complex x1 = createComplexFromKarthes(1,0);
struct complex x2 = createComplexFromKarthes(-1, 0);
struct complex x3 = createComplexFromKarthes(0, 1);
struct complex x4 = createComplexFromKarthes(0, -1);
resType[x + xRes * y] = 3;
int i = 0;
while (i < 30000 && fabs(z.r) < 10000){
z = subComplex(z, divComplex(subComplexScalar(powComplex(z, 4), 1), multComplexScalar(powComplex(z, 3), 4)));
i++;
if (compComplex(z, x1, 0.05)){
resType[x + xRes * y] = 0;
break;
} else if (compComplex(z, x2, 0.05)){
resType[x + xRes * y] = 1;
break;
} else if (compComplex(z, x3, 0.05)){
resType[x + xRes * y] = 2;
break;
}
}
if (fabs(z.r) >= 10000){
resType[x + xRes * y] = 4;
}
result[x + xRes * y] = i;
}
And here is the coloration of the image:
const int xRes = core->getXRes();
const int yRes = core->getYRes();
for (int y = 0; y < fraktal->getHeight(); y++){
for (int x = 0; x < fraktal->getWidth(); x++){
int conDiv = genCL->result[x + y * xRes];
int type = genCL->typeRes[x + y * xRes];
if (type == 0){
//converging to x1
fraktal->setPixel(x, y, 1*conDiv, 1*conDiv, 0, 1);
} else if (type == 1){
//converging to x2
fraktal->setPixel(x, y, 0, 0, 1*conDiv, 1);
} else if (type == 2){
//converging to x3
fraktal->setPixel(x, y, 0, 1*conDiv, 0, 1);
} else if (type == 3){
//diverging and interrupted by loop end
fraktal->setPixel(x, y, 1*conDiv, 0, 0, 1);
} else {
//diverging and interrupted by z.r > 10000
fraktal->setPixel(x, y, 1, 1, 1, 0.1*conDiv);
}
}
}
I had some mistakes in the complex number computations but I check everything today again and again.. I think they should be okay now.. but what else could be the reason that there are just this few start values converging? Did I do something wrong with newton's method?
Thanks for all your help!!
Well somewhat it really helped to run the code as normal C code.. as this makes Debugging easier: so the issue were some coding issues which I have been able to solve now.. for example my pow function was corrupted and when I added or subtracted I forgot to set the imaginary part to the temp complex number .. so here's my final OpenCL kernel:
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
#define pi 3.14159265359
struct complex {
double im;
double re;
double r;
double phi;
};
struct complex createComplexFromPolar(double _r, double _phi){
struct complex t;
t.r = _r;
t.phi = _phi;
t.re = _r*cos(_phi);
t.im = _r*sin(_phi);
return t;
}
struct complex createComplexFromKarthes(double real, double imag){
struct complex t;
t.re = real;
t.im = imag;
t.phi = atan2(imag, real);
t.r = sqrt(t.re*t.re + t.im*t.im);
return t;
}
struct complex recreateComplexFromKarthes(struct complex t){
return createComplexFromKarthes(t.re, t.im);
}
struct complex recreateComplexFromPolar(struct complex t){
return createComplexFromPolar(t.r, t.phi);
}
struct complex addComplex(const struct complex z, const struct complex c){
return createComplexFromKarthes(c.re + z.re, c.im + z.im);
}
struct complex subComplex(const struct complex z, const struct complex c){
return createComplexFromKarthes(z.re - c.re, z.im - c.im);
}
struct complex addComplexScalar(const struct complex z, const double n){
return createComplexFromKarthes(z.re + n,z.im);
}
struct complex subComplexScalar(const struct complex z, const double n){
return createComplexFromKarthes(z.re - n, z.im);
}
struct complex multComplexScalar(const struct complex z, const double n){
return createComplexFromKarthes(z.re * n,z.im * n);
}
struct complex multComplex(const struct complex z, const struct complex c) {
return createComplexFromKarthes(z.re*c.re-z.im*c.im, z.re*c.im+z.im*c.re);
//return createComplexFromPolar(z.r*c.r, z.phi + c.phi);
}
struct complex powComplex(const struct complex z, int i){
struct complex t = z;
for (int j = 0; j < i-1; j++){
t = multComplex(t, z);
}
return t;
}
struct complex divComplex(const struct complex z, const struct complex c) {
return createComplexFromPolar(z.r / c.r, z.phi-c.phi);
}
bool compComplex(const struct complex z, const struct complex c, float comp){
if (fabs(z.re - c.re) <= comp && fabs(z.im - c.im) <= comp)
return true;
return false;
}
__kernel void newtonFraktal(__global const int* res, __global const int* zoom, __global int* offset, __global const double* param, __global int* result, __global int* resType){
const int x = get_global_id(0) + offset[0];
const int y = get_global_id(1) + offset[1];
const int xRes = res[0];
const int yRes = res[1];
const double a = (x - (xRes / 2)) == 0 ? 0 : (double)((x - (double)(xRes / 2)) / zoom[0]);
const double b = (y - (yRes / 2)) == 0 ? 0 : (double)((y - (double)(yRes / 2)) / zoom[1]);
struct complex z = createComplexFromKarthes(a, b);
//struct complex c = createComplexFromKarthes(param[0], param[1]);
struct complex x1 = createComplexFromKarthes(0.7071068, 0.7071068);
struct complex x2 = createComplexFromKarthes(0.7071068, -0.7071068);
struct complex x3 = createComplexFromKarthes(-0.7071068, 0.7071068);
struct complex x4 = createComplexFromKarthes(-0.7071068, -0.7071068);
struct complex f, d;
resType[x + xRes * y] = 11;
int i = 0;
while (i < 6000 && fabs(z.r) < 10000){
f = addComplexScalar(powComplex(z, 4), 1);
d = multComplexScalar(powComplex(z, 3), 3);
z = subComplex(z, divComplex(f, d));
i++;
if (compComplex(z, x1, 0.0000001)){
resType[x + xRes * y] = 0;
break;
} else if (compComplex(z, x2, 0.0000001)){
resType[x + xRes * y] = 1;
break;
} else if (compComplex(z, x3, 0.0000001)){
resType[x + xRes * y] = 2;
break;
} else if (compComplex(z, x4, 0.0000001)){
resType[x + xRes * y] = 3;
break;
}
}
if (fabs(z.r) >= 1000){
resType[x + xRes * y] = 10;
}
result[x + xRes * y] = i;
}
hope it might help someone someday.. :)