numo-gsl-integration 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24601b3a87462ee4bc6077999599478d3c08776b8da2caa648e56dbfaacec731
4
- data.tar.gz: 97d7fe3772e374a35ab108ea0a4238fbbceb04598fc2637c3f4d76285cc17f66
3
+ metadata.gz: 57503e38512a38ad357142981868d2e2aea1a47743f27aee55d80625715eb794
4
+ data.tar.gz: d87dadfb371056e006cb1a914c6cebe81131cfa21c1e5f4daa35f72f377a98bf
5
5
  SHA512:
6
- metadata.gz: 86447bf3974787c1067ceecacf700b3583fc2365fb0699f7fac10ed33c54b03c83a9ea5467598c0f9f687222d8ad6fed5c14feeca617d61d7ad39f01accb9ea1
7
- data.tar.gz: 682f48569710825aac5fd2905dcfcab621abe47cea6f48f332b367d6da1747afe38512d08a6e84e1911b6ccd331d88fdb329b714bfa5619534ed8cea41227708
6
+ metadata.gz: ad109b9e20ce13a6eeb8b34b919c49a353eb5b27b158e74d1f9d149219fc200ca9a567417a12a4ea6dc66f247caf68bfe864d5cbaaf811b58fdacd8c8ef6ffbb
7
+ data.tar.gz: 6cab24c8607e941ab66eaa7f44db5492836c117649956979f148755df73792fd66c0baa794177276c02dd90e1e931d071cd0b19a174dd8d6dbb2c864da08e0cf
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.4.0] - 2026-11-06
6
+
7
+ ### Added in 0.4.0
8
+
9
+ - Added Lebedev quadrature for surface integration over the unit sphere.
10
+
11
+ ### Changed in 0.4.0
12
+
13
+ - Required GNU Scientific Library 2.8 or later.
14
+
5
15
  ## [0.3.1] - 2026-11-06
6
16
 
7
17
  ### Changed in 0.3.1
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  ## Requirements
6
6
 
7
7
  - [Ruby](https://www.ruby-lang.org/) 3.2 or later
8
- - GNU Scientific Library (GSL)
8
+ - GNU Scientific Library (GSL) 2.8 or later
9
9
  - `gsl-config` available in `PATH`
10
10
 
11
11
  GSL itself must be installed separately.
@@ -61,6 +61,7 @@ end
61
61
  | `romberg` | Finite | Smooth functions with extrapolated trapezoidal integration |
62
62
  | `glfixed` | Finite | Fixed-order Gauss-Legendre integration of smooth functions |
63
63
  | `fixed` | Rule-dependent | Fixed-order Gaussian quadrature with selectable weight functions |
64
+ | `lebedev` | Unit sphere | Lebedev quadrature over the surface of the unit sphere |
64
65
 
65
66
  See [Integration routines](docs/routines.md) for parameters and examples.
66
67
 
data/docs/development.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Development
2
2
 
3
+ Development and builds require GSL 2.8 or later. Confirm the installed version before building:
4
+
5
+ ```bash
6
+ gsl-config --version
7
+ ```
8
+
9
+ The extension configuration stops with an error if `gsl-config` reports an older version or if the Lebedev integration API is unavailable.
10
+
3
11
  After checking out the repository, install the dependencies:
4
12
 
5
13
  ```bash
data/docs/routines.md CHANGED
@@ -383,3 +383,28 @@ end
383
383
  ```
384
384
 
385
385
  The example result is `1.0 / 6.0`. Fixed point quadratures return only the integral. They do not accept absolute or relative error bounds and do not return an error estimate. Use an adaptive routine when the required order is not known in advance.
386
+
387
+ ## Lebedev integration on the unit sphere
388
+
389
+ GSL [Lebedev quadrature](https://www.gnu.org/software/gsl/doc/html/integration.html#integrating-on-the-unit-sphere) approximates a surface integral over the unit sphere. `Numo::GSL::Integration.lebedev` passes the Cartesian coordinates of each quadrature point to the block:
390
+
391
+ ```ruby
392
+ result = Numo::GSL::Integration.lebedev(26) do |x, y, z|
393
+ (x * x) + (y * y) + (z * z)
394
+ end
395
+ ```
396
+
397
+ Every point satisfies `x * x + y * y + z * z == 1` within floating-point precision, so the example integral has an exact value of `4.0 * Math::PI`.
398
+
399
+ The argument is the number of Lebedev nodes. GSL 2.8 provides the following node counts:
400
+
401
+ ```text
402
+ 6, 14, 26, 38, 50, 74, 86, 110,
403
+ 146, 170, 194, 230, 266, 302, 350, 434,
404
+ 590, 770, 974, 1202, 1454, 1730, 2030, 2354,
405
+ 2702, 3074, 3470, 3890, 4334, 4802, 5294, 5810
406
+ ```
407
+
408
+ The node count must be a positive integer and must identify one of the rules supplied by GSL. Unsupported node counts are reported as `Numo::GSL::GslError`.
409
+
410
+ Lebedev integration returns only the surface integral. It does not accept an error tolerance and does not return an error estimate. Increasing the node count selects a rule that integrates spherical harmonics to a higher degree exactly.
@@ -1,13 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "mkmf"
4
+ require "rubygems/version"
4
5
 
5
6
  gsl_config = find_executable("gsl-config")
6
7
  abort("gsl-config is required") unless gsl_config
7
8
 
9
+ minimum_gsl_version = Gem::Version.new("2.8")
10
+ gsl_version = Gem::Version.new(`#{gsl_config} --version`.strip)
11
+ abort("GSL 2.8 or later is required (found #{gsl_version})") if gsl_version < minimum_gsl_version
12
+
8
13
  $CFLAGS << " #{`#{gsl_config} --cflags`.strip}"
9
14
  $libs = "#{`#{gsl_config} --libs`.strip} #{$libs}"
10
15
 
11
- abort("gsl/gsl_integration.h is required") unless have_header("gsl/gsl_integration.h")
16
+ header = "gsl/gsl_integration.h"
17
+ abort("#{header} is required") unless have_header(header)
18
+ abort("GSL Lebedev integration support is required") unless have_func("gsl_integration_lebedev_alloc", header)
12
19
 
13
20
  create_makefile("numo/gsl/integration/integration")
@@ -45,4 +45,5 @@ void Init_integration(void) {
45
45
  rb_define_singleton_method(mIntegration, "romberg", integration_romberg, -1);
46
46
  rb_define_singleton_method(mIntegration, "glfixed", integration_glfixed, -1);
47
47
  rb_define_singleton_method(mIntegration, "fixed", integration_fixed, -1);
48
+ rb_define_singleton_method(mIntegration, "lebedev", integration_lebedev, 1);
48
49
  }
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include <gsl/gsl_errno.h>
5
5
  #include <gsl/gsl_integration.h>
6
+ #include <gsl/gsl_math.h>
6
7
  #include <ruby.h>
7
8
 
8
9
  typedef struct {
@@ -52,5 +53,6 @@ VALUE integration_cquad(int argc, VALUE *argv, VALUE self);
52
53
  VALUE integration_romberg(int argc, VALUE *argv, VALUE self);
53
54
  VALUE integration_glfixed(int argc, VALUE *argv, VALUE self);
54
55
  VALUE integration_fixed(int argc, VALUE *argv, VALUE self);
56
+ VALUE integration_lebedev(VALUE self, VALUE n_value);
55
57
 
56
58
  #endif
@@ -0,0 +1,103 @@
1
+ #include "integration_internal.h"
2
+
3
+ typedef struct {
4
+ VALUE block;
5
+ VALUE x;
6
+ VALUE y;
7
+ VALUE z;
8
+ VALUE exception;
9
+ gsl_integration_lebedev_workspace *workspace;
10
+ double result;
11
+ int state;
12
+ } integration_lebedev_params;
13
+
14
+ static VALUE integration_call_lebedev_block(VALUE data) {
15
+ integration_lebedev_params *params = (integration_lebedev_params *)data;
16
+ VALUE arguments[3] = {params->x, params->y, params->z};
17
+ VALUE value;
18
+
19
+ value = rb_funcallv(params->block, rb_intern("call"), 3, arguments);
20
+ return rb_Float(value);
21
+ }
22
+
23
+ static VALUE integration_free_lebedev_workspace(VALUE data) {
24
+ integration_lebedev_params *params = (integration_lebedev_params *)data;
25
+
26
+ if (params->workspace != NULL) {
27
+ gsl_integration_lebedev_free(params->workspace);
28
+ }
29
+
30
+ return Qnil;
31
+ }
32
+
33
+ static VALUE integration_run_lebedev(VALUE data) {
34
+ integration_lebedev_params *params = (integration_lebedev_params *)data;
35
+ double result = 0.0;
36
+ size_t i;
37
+
38
+ for (i = 0; i < params->workspace->n; ++i) {
39
+ VALUE value;
40
+
41
+ params->x = DBL2NUM(params->workspace->x[i]);
42
+ params->y = DBL2NUM(params->workspace->y[i]);
43
+ params->z = DBL2NUM(params->workspace->z[i]);
44
+ value = rb_protect(integration_call_lebedev_block, (VALUE)params,
45
+ &params->state);
46
+
47
+ if (params->state != 0) {
48
+ params->exception = rb_errinfo();
49
+ break;
50
+ }
51
+
52
+ result += params->workspace->weights[i] * NUM2DBL(value);
53
+ }
54
+
55
+ params->result = 4.0 * M_PI * result;
56
+ return Qnil;
57
+ }
58
+
59
+ static VALUE integration_run_lebedev_with_workspace(VALUE data) {
60
+ integration_lebedev_params *params = (integration_lebedev_params *)data;
61
+
62
+ return rb_ensure(integration_run_lebedev, data,
63
+ integration_free_lebedev_workspace, data);
64
+ }
65
+
66
+ VALUE integration_lebedev(VALUE self, VALUE n_value) {
67
+ integration_lebedev_params params = {0};
68
+ size_t n;
69
+ int state = 0;
70
+
71
+ (void)self;
72
+
73
+ if (!RB_INTEGER_TYPE_P(n_value)) {
74
+ rb_raise(rb_eTypeError, "n must be an Integer");
75
+ }
76
+ if (RTEST(rb_funcall(n_value, rb_intern("<="), 1, INT2FIX(0)))) {
77
+ rb_raise(rb_eArgError, "n must be positive");
78
+ }
79
+
80
+ n = NUM2SIZET(n_value);
81
+ params.block = rb_block_proc();
82
+ params.workspace = gsl_integration_lebedev_alloc(n);
83
+ if (params.workspace == NULL) {
84
+ rb_memerror();
85
+ }
86
+
87
+ rb_protect(integration_run_lebedev_with_workspace, (VALUE)&params, &state);
88
+
89
+ RB_GC_GUARD(params.block);
90
+ RB_GC_GUARD(params.x);
91
+ RB_GC_GUARD(params.y);
92
+ RB_GC_GUARD(params.z);
93
+ RB_GC_GUARD(params.exception);
94
+
95
+ if (params.state != 0) {
96
+ rb_exc_raise(params.exception);
97
+ }
98
+ if (state != 0) {
99
+ rb_jump_tag(state);
100
+ }
101
+
102
+ return DBL2NUM(params.result);
103
+ }
@@ -3,7 +3,7 @@
3
3
  module Numo
4
4
  module GSL
5
5
  module Integration
6
- VERSION = "0.3.1"
6
+ VERSION = "0.4.0"
7
7
  end
8
8
  end
9
9
  end
@@ -116,6 +116,9 @@ module Numo
116
116
  ?alpha: Numeric,
117
117
  ?beta: Numeric
118
118
  ) { (Float) -> Numeric } -> Float
119
+ def self.lebedev: (Integer n) {
120
+ (Float, Float, Float) -> Numeric
121
+ } -> Float
119
122
  end
120
123
  end
121
124
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: numo-gsl-integration
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takahiro SATOH
@@ -31,6 +31,7 @@ files:
31
31
  - ext/numo/gsl/integration/glfixed.c
32
32
  - ext/numo/gsl/integration/integration.c
33
33
  - ext/numo/gsl/integration/integration_internal.h
34
+ - ext/numo/gsl/integration/lebedev.c
34
35
  - ext/numo/gsl/integration/options.c
35
36
  - ext/numo/gsl/integration/qag.c
36
37
  - ext/numo/gsl/integration/qagi.c