0

我正在为Scientific Ruby Foundationfftw3创建一个 ruby​​ 包装器,它使用 nmatrix 对象而不是常规的 ruby​​ 数组。

我在返回转换后的数组时遇到了一个奇怪的问题,因为我不确定如何执行此操作,因此我可以检查转换是否已根据八度音程或(类似的)在我的规范中正确计算

我有一个想法,我可能最好将out作为fftw_complex类型的输出数组转换为 VALUE 以在返回之前将其传递给 nmatrix 对象,但我不确定我是否应该使用智慧并使用 fftw 从中获取值.

这是travis-ci 上规范输出的方法和链接

static VALUE
fftw_r2c_one(VALUE self, VALUE nmatrix)
{
  VALUE cNMatrix = rb_define_class("NMatrix", rb_cObject);

  fftw_plan plan;

  VALUE shape = rb_funcall(nmatrix, rb_intern("shape"), 0);

  const int size = NUM2INT(rb_funcall(cNMatrix, rb_intern("size"), 1, shape));

  double* in = ALLOC_N(double, size);

  for (int i = 0; i < size; i++)
  {
    in[i] = NUM2DBL(rb_funcall(nmatrix, rb_intern("[]"), 1, INT2FIX(i)));
    printf("IN[%d]: in[%.2f] \n", i, in[i]);
  }
  fftw_complex* out = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * size + 1);

  plan = fftw_plan_dft_r2c(1,&size, in, out, FFTW_ESTIMATE);

  fftw_execute(plan);

  fftw_destroy_plan(plan);
  xfree(in);
  fftw_free(out);
  return nmatrix;
}

如果您愿意,请随意从 github克隆 repo并尝试一下。

注意:在开始这个项目之前,我很陌生fftw3并且没有使用C(或)太多。ruby我已经更习惯了java,到目前为止,我还没有完全理解内存管理等较低级别的概念,但我正在接受这个项目pythonjavascript请在您的回答中牢记这一点,并尝试通过避免使用行话(或注意指出)来确保他们对最近主要习惯于面向对象的方法的人来说很清楚真的会有帮助。

谢谢你。

4

2 回答 2

0

我从Colin Fuller那里得到了一些建议,经过他的一些建议,我想出了这个解决方案:

VALUE fftw_complex_to_nm_complex(fftw_complex* in) {
    double real = ((double (*)) in)[1];
    double imag = ((double (*)) in)[2];
    VALUE mKernel = rb_define_module("Kernel");
    return rb_funcall(mKernel,
                      rb_intern("Complex"),
                      2,
                      rb_float_new(real),
                      rb_float_new(imag));
}

/**
  fftw_r2c
  @param self
  @param nmatrix
  @return nmatrix

  With FFTW_ESTIMATE as a flag in the plan,
  the input and and output are not overwritten at runtime
  The plan will use a heuristic approach to picking plans
  rather than take measurements
*/
static VALUE
fftw_r2c_one(VALUE self, VALUE nmatrix)
{
 /**
  Define and initialise the NMatrix class:
  The initialisation rb_define_class will
  just retrieve the NMatrix class that already exists
  or define a new class altogether if it does not
  find NMatrix. */
  VALUE cNMatrix = rb_define_class("NMatrix", rb_cObject);

  fftw_plan plan;

  const int rank = rb_iv_set(self, "@rank", 1);


  // shape is a ruby array, e.g. [2, 2] for a 2x2 matrix
  VALUE shape = rb_funcall(nmatrix, rb_intern("shape"), 0);
  // size is the number of elements stored for a matrix with dimensions = shape
  const int size = NUM2INT(rb_funcall(cNMatrix, rb_intern("size"), 1, shape));

  double* in = ALLOC_N(double, size);
  fftw_complex* out = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * size * size);

  for (int i = 0; i < size; i++)
  {
    in[i] = NUM2DBL(rb_funcall(nmatrix, rb_intern("[]"), 1, INT2FIX(i)));;
  }

  plan = fftw_plan_dft_r2c(1,&size, in, out, FFTW_ESTIMATE);
  fftw_execute(plan);

  for (int i = 0; i < 2; i++)
  {
    rb_funcall(nmatrix, rb_intern("[]="), 2, INT2FIX(i), fftw_complex_to_nm_complex(out + i));
  }

  // INFO: http://www.fftw.org/doc/New_002darray-Execute-Functions.html#New_002darray-Execute-Functions
  fftw_destroy_plan(plan);

  xfree(in);
  fftw_free(out);
  return nmatrix;
}

剩下的唯一问题是让规范识别我正在寻找在ruby​​ 核心复杂 API中解决的输出类型

于 2014-08-22T18:48:43.040 回答
0

如果您想看到使用 FFTW 带来的任何性能优势,那么您需要重构此代码,以便针对给定的 FFT 大小只执行一次计划生成,因为计划生成成本非常高,而执行计划是执行计划的地方性能收益来自于。

你也可以

  • a) 有两个入口点 - 一个生成计划的初始化例程,然后是执行计划的主入口点

  • b) 使用记忆技术,以便您只生成一次计划,第一次为给定的 FFT 维度调用您,然后缓存该计划以供后续重用。

b) 的优点是它是一个更简洁的实现,只有一个入口点;缺点是如果您调用具有频繁变化的维度的函数,它会中断。

于 2014-08-21T07:58:40.107 回答