carray 3.0.1 → 3.0.3
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 +4 -4
- data/CHANGELOG.md +824 -0
- data/README.md +5 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +258 -4
- data/ext/ca_axis_group.c +368 -184
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +90 -41
- data/ext/ca_compare.c +8 -2
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +27 -11
- data/ext/ca_kernel_iterator.c +960 -245
- data/ext/ca_kernel_iterator.h +352 -181
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +194 -42
- data/ext/ca_obj_bincmp.c +152 -121
- data/ext/ca_obj_binop.c +107 -210
- data/ext/ca_obj_bitarray.c +37 -24
- data/ext/ca_obj_bitfield.c +95 -62
- data/ext/ca_obj_block.c +5 -4
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +180 -50
- data/ext/ca_obj_face.c +428 -62
- data/ext/ca_obj_face.h +107 -47
- data/ext/ca_obj_fake.c +21 -50
- data/ext/ca_obj_farray.c +1 -0
- data/ext/ca_obj_field.c +16 -3
- data/ext/ca_obj_fixlen_string.c +39 -26
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +235 -55
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +551 -233
- data/ext/ca_obj_record.c +51 -16
- data/ext/ca_obj_reduce.c +5 -8
- data/ext/ca_obj_refer.c +117 -33
- data/ext/ca_obj_remap.c +57 -17
- data/ext/ca_obj_repeat.c +21 -5
- data/ext/ca_obj_roll.c +115 -11
- data/ext/ca_obj_select.c +88 -75
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -123
- data/ext/ca_obj_stride.c +267 -35
- data/ext/ca_obj_string.c +25 -22
- data/ext/ca_obj_tile.c +32 -16
- data/ext/ca_obj_time.c +23 -25
- data/ext/ca_obj_timedelta.c +21 -23
- data/ext/ca_obj_transpose.c +12 -3
- data/ext/ca_obj_triop.c +110 -117
- data/ext/ca_obj_window.c +157 -29
- data/ext/ca_op_ipower.c +25 -13
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +385 -198
- data/ext/ca_sweep_engine.h +31 -23
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +463 -0
- data/ext/carray.h +90 -28
- data/ext/carray_access.c +487 -231
- data/ext/carray_address_basis.c +731 -0
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +49 -17
- data/ext/carray_call_cfunc.c +667 -511
- data/ext/carray_cast.c +1556 -247
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +84 -38
- data/ext/carray_core.c +591 -119
- data/ext/carray_count.c +148 -96
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +604 -291
- data/ext/carray_generate.c +76 -40
- data/ext/carray_histogram.c +152 -94
- data/ext/carray_hold.c +2 -1
- data/ext/carray_index_classifier.c +63 -96
- data/ext/carray_internal.h +400 -1
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +88 -66
- data/ext/carray_mask.c +705 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +263 -63
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1093 -363
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +582 -204
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +183 -70
- data/ext/carray_sort.c +235 -240
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +99 -21
- data/ext/carray_test.c +167 -64
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_utils.c +363 -98
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +103 -118
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1349 -851
- data/ext/ruby_carray.c +17 -2
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +39 -0
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +18 -0
- data/lib/carray/axis_group.rb +245 -20
- data/lib/carray/basics.rb +95 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +200 -54
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +244 -59
- data/lib/carray/categorical_iterator.rb +391 -757
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +177 -27
- data/lib/carray/construct.rb +40 -0
- data/lib/carray/data_type_extension.rb +3 -0
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +590 -76
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +205 -35
- data/lib/carray/frame/group.rb +199 -40
- data/lib/carray/frame/io.rb +452 -60
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +54 -19
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +726 -56
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +111 -2
- data/lib/carray/fusion.rb +229 -38
- data/lib/carray/inspect.rb +71 -20
- data/lib/carray/iterator.rb +157 -2
- data/lib/carray/lazy.rb +54 -67
- data/lib/carray/mask_gap_fill.rb +66 -50
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +40 -7
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bincount.rb +2 -2
- data/lib/carray/methods/bit_string.rb +14 -3
- data/lib/carray/methods/composition.rb +16 -10
- data/lib/carray/methods/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/gather_nd.rb +13 -2
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_in.rb +130 -6
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +235 -21
- data/lib/carray/methods/mask_duplicates.rb +35 -1
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +22 -1
- data/lib/carray/methods/repeat.rb +91 -0
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +52 -3
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +14 -11
- data/lib/carray/segment_iterator.rb +990 -0
- data/lib/carray/serialize.rb +197 -27
- data/lib/carray/slab_iterator.rb +109 -40
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +9 -4
- data/lib/carray/struct.rb +142 -51
- data/lib/carray/struct_builder.rb +110 -14
- data/lib/carray/table.rb +2 -2
- data/lib/carray/time.rb +535 -105
- data/lib/carray/window_iterator.rb +259 -82
- data/lib/carray.rb +4 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_block.rb +2 -7
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +1 -1
- data/yard-stubs/ca_obj_refer.rb +3 -3
- data/yard-stubs/ca_obj_roll.rb +0 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +11 -4
- data/yard-stubs/carray_access.rb +6 -5
- data/yard-stubs/carray_broadcast.rb +2 -2
- data/yard-stubs/carray_cast.rb +78 -43
- data/yard-stubs/carray_conversion.rb +13 -1
- data/yard-stubs/carray_copy.rb +8 -3
- data/yard-stubs/carray_core.rb +3 -83
- data/yard-stubs/carray_count.rb +16 -2
- data/yard-stubs/carray_lazy.rb +210 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +1499 -4
- data/yard-stubs/carray_median_percentile.rb +26 -8
- data/yard-stubs/carray_order.rb +43 -82
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +11 -8
- data/yard-stubs/carray_sort.rb +50 -8
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -19
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_build_flags.h +0 -3
- data/ext/carray_config.h +0 -28
- data/ext/carray_kernels_bincmp.c +0 -4446
- data/ext/carray_kernels_binop.c +0 -11001
- data/ext/carray_kernels_init.c +0 -1131
- data/ext/carray_kernels_map.c +0 -3467
- data/ext/carray_kernels_moncmp.c +0 -2097
- data/ext/carray_kernels_monop.c +0 -18313
- data/ext/carray_kernels_reduce_aggregate.c +0 -25837
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14593
- data/ext/carray_kernels_reduce_extreme.c +0 -16948
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32138
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_random.c
CHANGED
|
@@ -18,139 +18,159 @@
|
|
|
18
18
|
---------------------------------------------------------------------------- */
|
|
19
19
|
|
|
20
20
|
#include "carray.h"
|
|
21
|
+
#include "carray_internal.h" /* ca_attach_window */
|
|
21
22
|
#include <math.h>
|
|
22
23
|
#include <string.h>
|
|
24
|
+
#include <stdint.h>
|
|
25
|
+
|
|
26
|
+
/* The generator itself, shared verbatim with carray-jit. See the file's
|
|
27
|
+
own comment for why it carries no include guard. */
|
|
28
|
+
#include "ca_rng_normal.h"
|
|
29
|
+
#include "ca_rng_xoshiro256pp.h"
|
|
30
|
+
|
|
31
|
+
VALUE rb_cCARng;
|
|
32
|
+
|
|
33
|
+
/* Where a fill's numbers come from. Resolved once per call rather than
|
|
34
|
+
per cell: the `rng:` argument is one object for the whole array, and
|
|
35
|
+
asking what it is inside the loop would put a Ruby type test between
|
|
36
|
+
every pair of draws.
|
|
37
|
+
|
|
38
|
+
CA_RNG_OWN holds the state's cells directly. That array belongs to
|
|
39
|
+
the CArray::Rng the caller passed, so the draws advance it and the
|
|
40
|
+
next call -- here or in a kernel -- carries on from where this one
|
|
41
|
+
stopped. */
|
|
42
|
+
enum {
|
|
43
|
+
CA_RNG_DEFAULT = 0, /* no rng: given -- the per-ractor MT */
|
|
44
|
+
CA_RNG_RUBY = 1, /* a ::Random instance -- also MT */
|
|
45
|
+
CA_RNG_OWN = 2 /* a CArray::Rng -- the generator above */
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
typedef struct {
|
|
49
|
+
int kind;
|
|
50
|
+
VALUE rng;
|
|
51
|
+
CArray *state; /* the CArray::Rng state for CA_RNG_OWN, else NULL */
|
|
52
|
+
int64_t *cells; /* state->ptr, once the window has attached it */
|
|
53
|
+
} ca_rng_t;
|
|
23
54
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
ca_random_real(VALUE rng)
|
|
55
|
+
static void
|
|
56
|
+
ca_rng_resolve (VALUE rng, ca_rng_t *source)
|
|
27
57
|
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
58
|
+
source->rng = rng;
|
|
59
|
+
source->state = NULL;
|
|
60
|
+
source->cells = NULL;
|
|
61
|
+
if (NIL_P(rng)) {
|
|
62
|
+
source->kind = CA_RNG_DEFAULT;
|
|
63
|
+
}
|
|
64
|
+
else if (rb_obj_is_kind_of(rng, rb_cCARng)) {
|
|
65
|
+
VALUE state = rb_ivar_get(rng, rb_intern("@state"));
|
|
66
|
+
CArray *ca;
|
|
67
|
+
TypedData_Get_Struct(state, CArray, &carray_data_type, ca);
|
|
68
|
+
source->kind = CA_RNG_OWN;
|
|
69
|
+
source->state = ca;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
source->kind = CA_RNG_RUBY;
|
|
73
|
+
}
|
|
32
74
|
}
|
|
33
75
|
|
|
34
|
-
|
|
35
|
-
|
|
76
|
+
/* The fills below draw inside a window over the array and, for a
|
|
77
|
+
CArray::Rng, the generator's state. A Ruby generator (rng: a
|
|
78
|
+
::Random or anything with #rand) is called per draw and can raise part
|
|
79
|
+
way; the window then pushes back the cells drawn so far, and the state
|
|
80
|
+
they advanced, as it does when the fill completes. */
|
|
81
|
+
|
|
82
|
+
typedef struct {
|
|
83
|
+
CArray *ca;
|
|
84
|
+
ca_rng_t source;
|
|
85
|
+
ca_size_t n;
|
|
86
|
+
int is_default;
|
|
87
|
+
double low_dbl, high_dbl;
|
|
88
|
+
long low_long;
|
|
89
|
+
unsigned long limit;
|
|
90
|
+
int axis; /* shuffle!: -1 = all cells */
|
|
91
|
+
} ca_random_ctx_t;
|
|
92
|
+
|
|
93
|
+
static void
|
|
94
|
+
ca_random_run (ca_random_ctx_t *c, VALUE rng, VALUE (*fill)(VALUE))
|
|
36
95
|
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
96
|
+
CArray *list[2];
|
|
97
|
+
ca_rng_resolve(rng, &c->source);
|
|
98
|
+
list[0] = c->ca;
|
|
99
|
+
list[1] = c->source.state;
|
|
100
|
+
ca_attach_window(2, list, "ww", fill, (VALUE) c);
|
|
41
101
|
}
|
|
42
102
|
|
|
43
|
-
/*
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* numbers in-place, returning self. Range surface mirrors Numo/NumPy
|
|
47
|
-
* (half-open [low, high) as the default) plus Ruby idiom (`..` closed,
|
|
48
|
-
* `...` half-open):
|
|
49
|
-
*
|
|
50
|
-
* random! float [0.0, 1.0); integer -> ArgumentError
|
|
51
|
-
* random!(high) [0, high) (Ruby rand shorthand)
|
|
52
|
-
* random!(low, high) [low, high) (Numo positional)
|
|
53
|
-
* random!(a..b) [a, b] closed (integer: b included)
|
|
54
|
-
* random!(a...b) [a, b) half-open
|
|
55
|
-
* random!(..., rng: r) any of the above with a custom Random source
|
|
56
|
-
*
|
|
57
|
-
* For float, `..` and `...` return the same distribution (endpoint
|
|
58
|
-
* probability ~2^-53); the closed form is honored syntactically but not
|
|
59
|
-
* enforced at the mantissa level, matching NumPy/SciPy convention.
|
|
60
|
-
* Boolean fills 0/1 with 50% probability each and ignores the range
|
|
61
|
-
* argument. Rejects CA_OBJECT / CA_FIXLEN. */
|
|
62
|
-
static VALUE
|
|
63
|
-
rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
|
|
103
|
+
/* The state is attached once the window is open. */
|
|
104
|
+
static void
|
|
105
|
+
ca_rng_bind (ca_rng_t *source)
|
|
64
106
|
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
VALUE low_val = Qnil, high_val = Qnil;
|
|
68
|
-
int is_default = 1;
|
|
69
|
-
int high_is_closed = 0;
|
|
70
|
-
double low_dbl = 0.0, high_dbl = 0.0;
|
|
71
|
-
long low_long = 0, high_long = 0;
|
|
72
|
-
unsigned long limit = 0;
|
|
73
|
-
ca_size_t i, n;
|
|
74
|
-
VALUE rng = Qnil;
|
|
75
|
-
|
|
76
|
-
rb_scan_args(argc, argv, "02:", &arg1, &arg2, &opts);
|
|
77
|
-
rb_scan_options(opts, "rng", &rng);
|
|
107
|
+
source->cells = source->state ? (int64_t *) source->state->ptr : NULL;
|
|
108
|
+
}
|
|
78
109
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
high_val = rb_funcall(arg1, rb_intern("end"), 0);
|
|
87
|
-
if (NIL_P(low_val) || NIL_P(high_val)) {
|
|
88
|
-
rb_raise(rb_eArgError,
|
|
89
|
-
"random: Range must have finite begin and end");
|
|
90
|
-
}
|
|
91
|
-
high_is_closed = ! RTEST(rb_funcall(arg1, rb_intern("exclude_end?"), 0));
|
|
92
|
-
is_default = 0;
|
|
93
|
-
} else if (!NIL_P(arg1) && !NIL_P(arg2)) {
|
|
94
|
-
low_val = arg1;
|
|
95
|
-
high_val = arg2;
|
|
96
|
-
is_default = 0;
|
|
97
|
-
} else if (!NIL_P(arg1)) {
|
|
98
|
-
low_val = INT2FIX(0);
|
|
99
|
-
high_val = arg1;
|
|
100
|
-
is_default = 0;
|
|
110
|
+
static inline double
|
|
111
|
+
ca_random_real (ca_rng_t *source)
|
|
112
|
+
{
|
|
113
|
+
switch (source->kind) {
|
|
114
|
+
case CA_RNG_OWN: return ca_xoshiro256pp_next_real(source->cells);
|
|
115
|
+
case CA_RNG_RUBY: return rb_random_real(source->rng);
|
|
116
|
+
default: return rb_genrand_real();
|
|
101
117
|
}
|
|
118
|
+
}
|
|
102
119
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
120
|
+
/* A draw in [0, limit], inclusive, matching what Ruby's own bounded draw
|
|
121
|
+
promises. For the generator above that is rejection sampling: taking
|
|
122
|
+
the remainder alone would favour the low end of the range whenever the
|
|
123
|
+
range does not divide 2**64. */
|
|
124
|
+
static inline uint64_t
|
|
125
|
+
ca_xoshiro256pp_below (int64_t *state, uint64_t range)
|
|
126
|
+
{
|
|
127
|
+
uint64_t threshold, draw;
|
|
128
|
+
if (range == 0) return ca_xoshiro256pp_next(state); /* the whole word */
|
|
129
|
+
threshold = (0 - range) % range; /* 2**64 mod range */
|
|
130
|
+
do {
|
|
131
|
+
draw = ca_xoshiro256pp_next(state);
|
|
132
|
+
} while (draw < threshold);
|
|
133
|
+
return draw % range;
|
|
134
|
+
}
|
|
110
135
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (low_long >= high_long) {
|
|
123
|
-
rb_raise(rb_eArgError,
|
|
124
|
-
"random: low must be less than high "
|
|
125
|
-
"(got low=%ld, high=%ld)",
|
|
126
|
-
low_long, high_long);
|
|
127
|
-
}
|
|
128
|
-
limit = (unsigned long)(high_long - low_long) - 1;
|
|
129
|
-
} else {
|
|
130
|
-
low_dbl = NUM2DBL(low_val);
|
|
131
|
-
high_dbl = NUM2DBL(high_val);
|
|
132
|
-
if (low_dbl >= high_dbl) {
|
|
133
|
-
rb_raise(rb_eArgError,
|
|
134
|
-
"random: low must be less than high "
|
|
135
|
-
"(got low=%g, high=%g)",
|
|
136
|
-
low_dbl, high_dbl);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
136
|
+
static inline unsigned long
|
|
137
|
+
ca_random_ulong_limited (ca_rng_t *source, unsigned long limit)
|
|
138
|
+
{
|
|
139
|
+
switch (source->kind) {
|
|
140
|
+
case CA_RNG_OWN:
|
|
141
|
+
return (unsigned long)
|
|
142
|
+
ca_xoshiro256pp_below(source->cells, (uint64_t) limit + 1);
|
|
143
|
+
case CA_RNG_RUBY:
|
|
144
|
+
return rb_random_ulong_limited(source->rng, limit);
|
|
145
|
+
default:
|
|
146
|
+
return rb_genrand_ulong_limited(limit);
|
|
139
147
|
}
|
|
148
|
+
}
|
|
140
149
|
|
|
141
|
-
|
|
142
|
-
ca_attach(ca);
|
|
150
|
+
/* ---- random! ----------------------------------------------------------- */
|
|
143
151
|
|
|
152
|
+
static VALUE
|
|
153
|
+
ca_random_bang_fill (VALUE arg)
|
|
154
|
+
{
|
|
155
|
+
ca_random_ctx_t *c = (ca_random_ctx_t *) arg;
|
|
156
|
+
CArray *ca = c->ca;
|
|
157
|
+
ca_size_t i, n = c->n;
|
|
158
|
+
int is_default = c->is_default;
|
|
159
|
+
double low_dbl = c->low_dbl, high_dbl = c->high_dbl;
|
|
160
|
+
long low_long = c->low_long;
|
|
161
|
+
unsigned long limit = c->limit;
|
|
162
|
+
|
|
163
|
+
ca_rng_bind(&c->source);
|
|
144
164
|
switch (ca->data_type) {
|
|
145
165
|
case CA_FLOAT64: {
|
|
146
166
|
double *p = (double *)ca->ptr;
|
|
147
167
|
if (is_default) {
|
|
148
168
|
for (i = 0; i < n; i++)
|
|
149
|
-
p[i] = ca_random_real(
|
|
169
|
+
p[i] = ca_random_real(&c->source);
|
|
150
170
|
} else {
|
|
151
171
|
double range = high_dbl - low_dbl;
|
|
152
172
|
for (i = 0; i < n; i++)
|
|
153
|
-
p[i] = low_dbl + ca_random_real(
|
|
173
|
+
p[i] = low_dbl + ca_random_real(&c->source) * range;
|
|
154
174
|
}
|
|
155
175
|
break;
|
|
156
176
|
}
|
|
@@ -158,11 +178,11 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
|
|
|
158
178
|
float *p = (float *)ca->ptr;
|
|
159
179
|
if (is_default) {
|
|
160
180
|
for (i = 0; i < n; i++)
|
|
161
|
-
p[i] = (float)ca_random_real(
|
|
181
|
+
p[i] = (float)ca_random_real(&c->source);
|
|
162
182
|
} else {
|
|
163
183
|
double range = high_dbl - low_dbl;
|
|
164
184
|
for (i = 0; i < n; i++)
|
|
165
|
-
p[i] = (float)(low_dbl + ca_random_real(
|
|
185
|
+
p[i] = (float)(low_dbl + ca_random_real(&c->source) * range);
|
|
166
186
|
}
|
|
167
187
|
break;
|
|
168
188
|
}
|
|
@@ -170,12 +190,12 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
|
|
|
170
190
|
double complex *p = (double complex *)ca->ptr;
|
|
171
191
|
if (is_default) {
|
|
172
192
|
for (i = 0; i < n; i++)
|
|
173
|
-
p[i] = ca_random_real(
|
|
193
|
+
p[i] = ca_random_real(&c->source) + ca_random_real(&c->source) * I;
|
|
174
194
|
} else {
|
|
175
195
|
double range = high_dbl - low_dbl;
|
|
176
196
|
for (i = 0; i < n; i++)
|
|
177
|
-
p[i] = (low_dbl + ca_random_real(
|
|
178
|
-
+ (low_dbl + ca_random_real(
|
|
197
|
+
p[i] = (low_dbl + ca_random_real(&c->source) * range)
|
|
198
|
+
+ (low_dbl + ca_random_real(&c->source) * range) * I;
|
|
179
199
|
}
|
|
180
200
|
break;
|
|
181
201
|
}
|
|
@@ -183,26 +203,24 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
|
|
|
183
203
|
float complex *p = (float complex *)ca->ptr;
|
|
184
204
|
if (is_default) {
|
|
185
205
|
for (i = 0; i < n; i++)
|
|
186
|
-
p[i] = (float)ca_random_real(
|
|
206
|
+
p[i] = (float)ca_random_real(&c->source) + (float)ca_random_real(&c->source) * I;
|
|
187
207
|
} else {
|
|
188
208
|
double range = high_dbl - low_dbl;
|
|
189
209
|
for (i = 0; i < n; i++)
|
|
190
|
-
p[i] = (float)(low_dbl + ca_random_real(
|
|
191
|
-
+ (float)(low_dbl + ca_random_real(
|
|
210
|
+
p[i] = (float)(low_dbl + ca_random_real(&c->source) * range)
|
|
211
|
+
+ (float)(low_dbl + ca_random_real(&c->source) * range) * I;
|
|
192
212
|
}
|
|
193
213
|
break;
|
|
194
214
|
}
|
|
195
215
|
case CA_BOOLEAN: {
|
|
196
216
|
boolean8_t *p = (boolean8_t *)ca->ptr;
|
|
197
217
|
for (i = 0; i < n; i++)
|
|
198
|
-
p[i] = (ca_random_real(
|
|
218
|
+
p[i] = (ca_random_real(&c->source) < 0.5) ? 1 : 0;
|
|
199
219
|
break;
|
|
200
220
|
}
|
|
201
221
|
default: {
|
|
202
222
|
/* integer types: CA_INT8..CA_UINT64 */
|
|
203
223
|
if (is_default) {
|
|
204
|
-
ca_sync(ca);
|
|
205
|
-
ca_detach(ca);
|
|
206
224
|
rb_raise(rb_eArgError,
|
|
207
225
|
"random! on an integer array requires a range: "
|
|
208
226
|
"a.random!(high), a.random!(low, high), or "
|
|
@@ -212,54 +230,52 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
|
|
|
212
230
|
case CA_INT8: {
|
|
213
231
|
int8_t *p = (int8_t *)ca->ptr;
|
|
214
232
|
for (i = 0; i < n; i++)
|
|
215
|
-
p[i] = (int8_t)(low_long + ca_random_ulong_limited(
|
|
233
|
+
p[i] = (int8_t)(low_long + ca_random_ulong_limited(&c->source, limit));
|
|
216
234
|
break;
|
|
217
235
|
}
|
|
218
236
|
case CA_UINT8: {
|
|
219
237
|
uint8_t *p = (uint8_t *)ca->ptr;
|
|
220
238
|
for (i = 0; i < n; i++)
|
|
221
|
-
p[i] = (uint8_t)(low_long + ca_random_ulong_limited(
|
|
239
|
+
p[i] = (uint8_t)(low_long + ca_random_ulong_limited(&c->source, limit));
|
|
222
240
|
break;
|
|
223
241
|
}
|
|
224
242
|
case CA_INT16: {
|
|
225
243
|
int16_t *p = (int16_t *)ca->ptr;
|
|
226
244
|
for (i = 0; i < n; i++)
|
|
227
|
-
p[i] = (int16_t)(low_long + ca_random_ulong_limited(
|
|
245
|
+
p[i] = (int16_t)(low_long + ca_random_ulong_limited(&c->source, limit));
|
|
228
246
|
break;
|
|
229
247
|
}
|
|
230
248
|
case CA_UINT16: {
|
|
231
249
|
uint16_t *p = (uint16_t *)ca->ptr;
|
|
232
250
|
for (i = 0; i < n; i++)
|
|
233
|
-
p[i] = (uint16_t)(low_long + ca_random_ulong_limited(
|
|
251
|
+
p[i] = (uint16_t)(low_long + ca_random_ulong_limited(&c->source, limit));
|
|
234
252
|
break;
|
|
235
253
|
}
|
|
236
254
|
case CA_INT32: {
|
|
237
255
|
int32_t *p = (int32_t *)ca->ptr;
|
|
238
256
|
for (i = 0; i < n; i++)
|
|
239
|
-
p[i] = (int32_t)(low_long + ca_random_ulong_limited(
|
|
257
|
+
p[i] = (int32_t)(low_long + ca_random_ulong_limited(&c->source, limit));
|
|
240
258
|
break;
|
|
241
259
|
}
|
|
242
260
|
case CA_UINT32: {
|
|
243
261
|
uint32_t *p = (uint32_t *)ca->ptr;
|
|
244
262
|
for (i = 0; i < n; i++)
|
|
245
|
-
p[i] = (uint32_t)(low_long + ca_random_ulong_limited(
|
|
263
|
+
p[i] = (uint32_t)(low_long + ca_random_ulong_limited(&c->source, limit));
|
|
246
264
|
break;
|
|
247
265
|
}
|
|
248
266
|
case CA_INT64: {
|
|
249
267
|
int64_t *p = (int64_t *)ca->ptr;
|
|
250
268
|
for (i = 0; i < n; i++)
|
|
251
|
-
p[i] = (int64_t)(low_long + ca_random_ulong_limited(
|
|
269
|
+
p[i] = (int64_t)(low_long + ca_random_ulong_limited(&c->source, limit));
|
|
252
270
|
break;
|
|
253
271
|
}
|
|
254
272
|
case CA_UINT64: {
|
|
255
273
|
uint64_t *p = (uint64_t *)ca->ptr;
|
|
256
274
|
for (i = 0; i < n; i++)
|
|
257
|
-
p[i] = (uint64_t)(low_long + ca_random_ulong_limited(
|
|
275
|
+
p[i] = (uint64_t)(low_long + ca_random_ulong_limited(&c->source, limit));
|
|
258
276
|
break;
|
|
259
277
|
}
|
|
260
278
|
default:
|
|
261
|
-
ca_sync(ca);
|
|
262
|
-
ca_detach(ca);
|
|
263
279
|
rb_raise(rb_eCADataTypeError,
|
|
264
280
|
"random! is not supported for this data type");
|
|
265
281
|
}
|
|
@@ -267,64 +283,213 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
|
|
|
267
283
|
}
|
|
268
284
|
}
|
|
269
285
|
|
|
270
|
-
|
|
271
|
-
|
|
286
|
+
return Qnil;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* CArray#random!([low,] [high], rng:) — fill self with uniform random
|
|
290
|
+
* numbers in-place, returning self. Range surface mirrors Numo/NumPy
|
|
291
|
+
* (half-open [low, high) as the default) plus Ruby idiom (`..` closed,
|
|
292
|
+
* `...` half-open):
|
|
293
|
+
*
|
|
294
|
+
* random! float [0.0, 1.0); integer -> ArgumentError
|
|
295
|
+
* random!(high) [0, high) (Ruby rand shorthand)
|
|
296
|
+
* random!(low, high) [low, high) (Numo positional)
|
|
297
|
+
* random!(a..b) [a, b] closed (integer: b included)
|
|
298
|
+
* random!(a...b) [a, b) half-open
|
|
299
|
+
* random!(..., rng: r) any of the above with a custom Random source
|
|
300
|
+
*
|
|
301
|
+
* For float, `..` and `...` return the same distribution (endpoint
|
|
302
|
+
* probability ~2^-53); the closed form is honored syntactically but not
|
|
303
|
+
* enforced at the mantissa level, matching NumPy/SciPy convention.
|
|
304
|
+
* Boolean fills 0/1 with 50% probability each and ignores the range
|
|
305
|
+
* argument. Rejects CA_OBJECT / CA_FIXLEN. */
|
|
306
|
+
static VALUE
|
|
307
|
+
rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
|
|
308
|
+
{
|
|
309
|
+
CArray *ca;
|
|
310
|
+
VALUE arg1 = Qnil, arg2 = Qnil, opts = Qnil;
|
|
311
|
+
VALUE low_val = Qnil, high_val = Qnil;
|
|
312
|
+
int is_default = 1;
|
|
313
|
+
int high_is_closed = 0;
|
|
314
|
+
double low_dbl = 0.0, high_dbl = 0.0;
|
|
315
|
+
long low_long = 0, high_long = 0;
|
|
316
|
+
unsigned long limit = 0;
|
|
317
|
+
VALUE rng = Qnil;
|
|
318
|
+
|
|
319
|
+
rb_scan_args(argc, argv, "02:", &arg1, &arg2, &opts);
|
|
320
|
+
rb_scan_options(opts, "rng", &rng);
|
|
321
|
+
|
|
322
|
+
/* Parse positional args into (low_val, high_val) + high_is_closed. */
|
|
323
|
+
if (rb_obj_is_kind_of(arg1, rb_cRange)) {
|
|
324
|
+
if (!NIL_P(arg2)) {
|
|
325
|
+
rb_raise(rb_eArgError,
|
|
326
|
+
"random: cannot combine a Range with a second positional arg");
|
|
327
|
+
}
|
|
328
|
+
low_val = rb_funcall(arg1, rb_intern("begin"), 0);
|
|
329
|
+
high_val = rb_funcall(arg1, rb_intern("end"), 0);
|
|
330
|
+
if (NIL_P(low_val) || NIL_P(high_val)) {
|
|
331
|
+
rb_raise(rb_eArgError,
|
|
332
|
+
"random: Range must have finite begin and end");
|
|
333
|
+
}
|
|
334
|
+
high_is_closed = ! RTEST(rb_funcall(arg1, rb_intern("exclude_end?"), 0));
|
|
335
|
+
is_default = 0;
|
|
336
|
+
} else if (!NIL_P(arg1) && !NIL_P(arg2)) {
|
|
337
|
+
low_val = arg1;
|
|
338
|
+
high_val = arg2;
|
|
339
|
+
is_default = 0;
|
|
340
|
+
} else if (!NIL_P(arg1)) {
|
|
341
|
+
low_val = INT2FIX(0);
|
|
342
|
+
high_val = arg1;
|
|
343
|
+
is_default = 0;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
rb_ca_modify(self);
|
|
347
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
348
|
+
|
|
349
|
+
if (ca->data_type == CA_OBJECT || ca->data_type == CA_FIXLEN) {
|
|
350
|
+
rb_raise(rb_eCADataTypeError,
|
|
351
|
+
"random! is not supported for object/fixlen arrays");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/* Materialize (low, high) as the correct scalar type and validate.
|
|
355
|
+
* For integer data types, `..` closed adds 1 to high (turns into half-open
|
|
356
|
+
* for the sampler); for float data types, closed and half-open are
|
|
357
|
+
* equivalent so no adjustment. */
|
|
358
|
+
if (!is_default) {
|
|
359
|
+
int is_integer_type = (ca->data_type >= CA_INT8
|
|
360
|
+
&& ca->data_type <= CA_UINT64);
|
|
361
|
+
if (is_integer_type) {
|
|
362
|
+
low_long = NUM2LONG(low_val);
|
|
363
|
+
high_long = NUM2LONG(high_val);
|
|
364
|
+
if (high_is_closed) high_long += 1;
|
|
365
|
+
if (low_long >= high_long) {
|
|
366
|
+
rb_raise(rb_eArgError,
|
|
367
|
+
"random: low must be less than high "
|
|
368
|
+
"(got low=%ld, high=%ld)",
|
|
369
|
+
low_long, high_long);
|
|
370
|
+
}
|
|
371
|
+
limit = (unsigned long)(high_long - low_long) - 1;
|
|
372
|
+
} else {
|
|
373
|
+
low_dbl = NUM2DBL(low_val);
|
|
374
|
+
high_dbl = NUM2DBL(high_val);
|
|
375
|
+
if (low_dbl >= high_dbl) {
|
|
376
|
+
rb_raise(rb_eArgError,
|
|
377
|
+
"random: low must be less than high "
|
|
378
|
+
"(got low=%g, high=%g)",
|
|
379
|
+
low_dbl, high_dbl);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
{
|
|
385
|
+
ca_random_ctx_t c;
|
|
386
|
+
c.ca = ca;
|
|
387
|
+
c.n = ca->elements;
|
|
388
|
+
c.is_default = is_default;
|
|
389
|
+
c.low_dbl = low_dbl;
|
|
390
|
+
c.high_dbl = high_dbl;
|
|
391
|
+
c.low_long = low_long;
|
|
392
|
+
c.limit = limit;
|
|
393
|
+
ca_random_run(&c, rng, ca_random_bang_fill);
|
|
394
|
+
}
|
|
272
395
|
return self;
|
|
273
396
|
}
|
|
274
397
|
|
|
275
398
|
/* ---- randomn! ---------------------------------------------------------- */
|
|
276
399
|
|
|
400
|
+
/* One standard normal from the resolved source.
|
|
401
|
+
*
|
|
402
|
+
* For CArray::Rng this is two draws and no spare, which is what
|
|
403
|
+
* ca_rng_normal is and what lets a kernel's `randomn` continue a
|
|
404
|
+
* `randomn!` -- both are this function, called once per cell.
|
|
405
|
+
*
|
|
406
|
+
* For Ruby's MT the paired form below is kept: nothing there draws one
|
|
407
|
+
* at a time, so there is no second caller to agree with, and changing it
|
|
408
|
+
* would change the numbers an existing call gets. */
|
|
409
|
+
static inline double
|
|
410
|
+
ca_random_normal (ca_rng_t *source)
|
|
411
|
+
{
|
|
412
|
+
double u1 = ca_random_real(source);
|
|
413
|
+
double u2 = ca_random_real(source);
|
|
414
|
+
return ca_rng_normal(u1, u2);
|
|
415
|
+
}
|
|
416
|
+
|
|
277
417
|
static inline void
|
|
278
|
-
box_muller_pair(
|
|
418
|
+
box_muller_pair(ca_rng_t *source, double *r1, double *r2)
|
|
279
419
|
{
|
|
280
|
-
double u1 = ca_random_real(
|
|
281
|
-
double u2 = ca_random_real(
|
|
420
|
+
double u1 = ca_random_real(source);
|
|
421
|
+
double u2 = ca_random_real(source);
|
|
282
422
|
while (u1 == 0.0)
|
|
283
|
-
u1 = ca_random_real(
|
|
423
|
+
u1 = ca_random_real(source);
|
|
284
424
|
double r = sqrt(-2.0 * log(u1));
|
|
285
425
|
double theta = 2.0 * M_PI * u2;
|
|
286
426
|
*r1 = r * cos(theta);
|
|
287
427
|
*r2 = r * sin(theta);
|
|
288
428
|
}
|
|
289
429
|
|
|
290
|
-
/* CArray#randomn!(rng:) — fill self with standard normal N(0, 1)
|
|
291
|
-
* samples in-place via Box-Muller, returning self.
|
|
292
|
-
*
|
|
293
|
-
* Restricted to float / complex data types. Complex fills real + imag as
|
|
294
|
-
* two independent normals per cell. */
|
|
295
430
|
static VALUE
|
|
296
|
-
|
|
431
|
+
ca_randomn_bang_fill (VALUE arg)
|
|
297
432
|
{
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
ca_size_t i, n;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
433
|
+
ca_random_ctx_t *c = (ca_random_ctx_t *) arg;
|
|
434
|
+
CArray *ca = c->ca;
|
|
435
|
+
ca_size_t i, n = c->n;
|
|
436
|
+
|
|
437
|
+
ca_rng_bind(&c->source);
|
|
438
|
+
/* A CArray::Rng fills one cell per call rather than two, so that this
|
|
439
|
+
and a kernel drawing afterwards are the one sequence: both are
|
|
440
|
+
`ca_random_normal` run once per cell. It costs a draw a cell over
|
|
441
|
+
the paired form, and what it buys is that where the generator
|
|
442
|
+
stands can be worked out rather than run. */
|
|
443
|
+
if (c->source.kind == CA_RNG_OWN) {
|
|
444
|
+
switch (ca->data_type) {
|
|
445
|
+
case CA_FLOAT64: {
|
|
446
|
+
double *p = (double *)ca->ptr;
|
|
447
|
+
for (i = 0; i < n; i++)
|
|
448
|
+
p[i] = ca_random_normal(&c->source);
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
case CA_FLOAT32: {
|
|
452
|
+
float *p = (float *)ca->ptr;
|
|
453
|
+
for (i = 0; i < n; i++)
|
|
454
|
+
p[i] = (float) ca_random_normal(&c->source);
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
case CA_CMPLX128: {
|
|
458
|
+
double complex *p = (double complex *)ca->ptr;
|
|
459
|
+
for (i = 0; i < n; i++) {
|
|
460
|
+
/* Into locals first: C does not say which order a call's
|
|
461
|
+
arguments are evaluated in, and these two advance a state. */
|
|
462
|
+
double re = ca_random_normal(&c->source);
|
|
463
|
+
double im = ca_random_normal(&c->source);
|
|
464
|
+
p[i] = re + im * I;
|
|
465
|
+
}
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
case CA_CMPLX64: {
|
|
469
|
+
float complex *p = (float complex *)ca->ptr;
|
|
470
|
+
for (i = 0; i < n; i++) {
|
|
471
|
+
double re = ca_random_normal(&c->source);
|
|
472
|
+
double im = ca_random_normal(&c->source);
|
|
473
|
+
p[i] = (float) re + (float) im * I;
|
|
474
|
+
}
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
default:
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
return Qnil;
|
|
313
481
|
}
|
|
314
482
|
|
|
315
|
-
n = ca->elements;
|
|
316
|
-
ca_attach(ca);
|
|
317
|
-
|
|
318
483
|
switch (ca->data_type) {
|
|
319
484
|
case CA_FLOAT64: {
|
|
320
485
|
double *p = (double *)ca->ptr;
|
|
321
486
|
ca_size_t pairs = n / 2;
|
|
322
487
|
for (i = 0; i < pairs; i++) {
|
|
323
|
-
box_muller_pair(
|
|
488
|
+
box_muller_pair(&c->source, &p[2*i], &p[2*i+1]);
|
|
324
489
|
}
|
|
325
490
|
if (n % 2 == 1) {
|
|
326
491
|
double r1, r2;
|
|
327
|
-
box_muller_pair(
|
|
492
|
+
box_muller_pair(&c->source, &r1, &r2);
|
|
328
493
|
p[n-1] = r1;
|
|
329
494
|
}
|
|
330
495
|
break;
|
|
@@ -334,13 +499,13 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
|
|
|
334
499
|
ca_size_t pairs = n / 2;
|
|
335
500
|
for (i = 0; i < pairs; i++) {
|
|
336
501
|
double r1, r2;
|
|
337
|
-
box_muller_pair(
|
|
502
|
+
box_muller_pair(&c->source, &r1, &r2);
|
|
338
503
|
p[2*i] = (float)r1;
|
|
339
504
|
p[2*i+1] = (float)r2;
|
|
340
505
|
}
|
|
341
506
|
if (n % 2 == 1) {
|
|
342
507
|
double r1, r2;
|
|
343
|
-
box_muller_pair(
|
|
508
|
+
box_muller_pair(&c->source, &r1, &r2);
|
|
344
509
|
p[n-1] = (float)r1;
|
|
345
510
|
}
|
|
346
511
|
break;
|
|
@@ -349,7 +514,7 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
|
|
|
349
514
|
double complex *p = (double complex *)ca->ptr;
|
|
350
515
|
for (i = 0; i < n; i++) {
|
|
351
516
|
double r1, r2;
|
|
352
|
-
box_muller_pair(
|
|
517
|
+
box_muller_pair(&c->source, &r1, &r2);
|
|
353
518
|
p[i] = r1 + r2 * I;
|
|
354
519
|
}
|
|
355
520
|
break;
|
|
@@ -358,7 +523,7 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
|
|
|
358
523
|
float complex *p = (float complex *)ca->ptr;
|
|
359
524
|
for (i = 0; i < n; i++) {
|
|
360
525
|
double r1, r2;
|
|
361
|
-
box_muller_pair(
|
|
526
|
+
box_muller_pair(&c->source, &r1, &r2);
|
|
362
527
|
p[i] = (float)r1 + (float)r2 * I;
|
|
363
528
|
}
|
|
364
529
|
break;
|
|
@@ -367,8 +532,39 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
|
|
|
367
532
|
break;
|
|
368
533
|
}
|
|
369
534
|
|
|
370
|
-
|
|
371
|
-
|
|
535
|
+
return Qnil;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/* CArray#randomn!(rng:) — fill self with standard normal N(0, 1)
|
|
539
|
+
* samples in-place via Box-Muller, returning self.
|
|
540
|
+
*
|
|
541
|
+
* Restricted to float / complex data types. Complex fills real + imag as
|
|
542
|
+
* two independent normals per cell. */
|
|
543
|
+
static VALUE
|
|
544
|
+
rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
|
|
545
|
+
{
|
|
546
|
+
CArray *ca;
|
|
547
|
+
VALUE opts = Qnil;
|
|
548
|
+
|
|
549
|
+
rb_scan_args(argc, argv, "0:", &opts);
|
|
550
|
+
VALUE rng = Qnil;
|
|
551
|
+
rb_scan_options(opts, "rng", &rng);
|
|
552
|
+
|
|
553
|
+
rb_ca_modify(self);
|
|
554
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
555
|
+
|
|
556
|
+
if (ca->data_type != CA_FLOAT64 && ca->data_type != CA_FLOAT32 &&
|
|
557
|
+
ca->data_type != CA_CMPLX128 && ca->data_type != CA_CMPLX64) {
|
|
558
|
+
rb_raise(rb_eCADataTypeError,
|
|
559
|
+
"randomn! requires float or complex array");
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
{
|
|
563
|
+
ca_random_ctx_t c;
|
|
564
|
+
c.ca = ca;
|
|
565
|
+
c.n = ca->elements;
|
|
566
|
+
ca_random_run(&c, rng, ca_randomn_bang_fill);
|
|
567
|
+
}
|
|
372
568
|
return self;
|
|
373
569
|
}
|
|
374
570
|
|
|
@@ -390,55 +586,35 @@ swap_chunks(char *a, char *b, ca_size_t chunk_bytes, char *tmp)
|
|
|
390
586
|
* as a byte chunk and swapped whole, so multi-dim slices move
|
|
391
587
|
* together). */
|
|
392
588
|
static VALUE
|
|
393
|
-
|
|
589
|
+
ca_shuffle_bang_fill (VALUE arg)
|
|
394
590
|
{
|
|
395
|
-
|
|
396
|
-
|
|
591
|
+
ca_random_ctx_t *c = (ca_random_ctx_t *) arg;
|
|
592
|
+
CArray *ca = c->ca;
|
|
397
593
|
ca_size_t n;
|
|
398
|
-
int axis = -1;
|
|
399
|
-
|
|
400
|
-
rb_scan_args(argc, argv, "0:", &opts);
|
|
401
|
-
VALUE rng = Qnil;
|
|
402
|
-
rb_scan_options(opts, "rng,axis", &rng, &v_axis);
|
|
403
594
|
|
|
404
|
-
|
|
405
|
-
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
406
|
-
|
|
407
|
-
if (ca->elements <= 1) return self;
|
|
408
|
-
|
|
409
|
-
ca_attach(ca);
|
|
595
|
+
ca_rng_bind(&c->source);
|
|
410
596
|
|
|
411
|
-
if (
|
|
597
|
+
if (c->axis < 0) {
|
|
412
598
|
/* shuffle all elements */
|
|
413
|
-
|
|
599
|
+
volatile VALUE tmp_holder;
|
|
414
600
|
ca_size_t elem_bytes = ca->bytes;
|
|
415
|
-
char *tmp = (char
|
|
601
|
+
char *tmp = ALLOCV_N(char, tmp_holder, elem_bytes);
|
|
416
602
|
char *p = ca->ptr;
|
|
417
603
|
|
|
604
|
+
n = ca->elements;
|
|
418
605
|
for (ca_size_t i = n - 1; i > 0; i--) {
|
|
419
|
-
unsigned long j = ca_random_ulong_limited(
|
|
606
|
+
unsigned long j = ca_random_ulong_limited(&c->source, (unsigned long)i);
|
|
420
607
|
if ((ca_size_t)j != i) {
|
|
421
608
|
swap_chunks(p + i * elem_bytes, p + j * elem_bytes, elem_bytes, tmp);
|
|
422
609
|
}
|
|
423
610
|
}
|
|
424
|
-
|
|
611
|
+
ALLOCV_END(tmp_holder);
|
|
425
612
|
}
|
|
426
613
|
else {
|
|
427
|
-
axis =
|
|
428
|
-
|
|
429
|
-
if (axis < 0 || axis >= ca->ndim) {
|
|
430
|
-
ca_sync(ca);
|
|
431
|
-
ca_detach(ca);
|
|
432
|
-
rb_raise(rb_eArgError,
|
|
433
|
-
"axis %d is out of range for ndim %d", axis, ca->ndim);
|
|
434
|
-
}
|
|
614
|
+
int axis = c->axis;
|
|
615
|
+
volatile VALUE tmp_holder;
|
|
435
616
|
|
|
436
617
|
n = ca->dim[axis];
|
|
437
|
-
if (n <= 1) {
|
|
438
|
-
ca_sync(ca);
|
|
439
|
-
ca_detach(ca);
|
|
440
|
-
return self;
|
|
441
|
-
}
|
|
442
618
|
|
|
443
619
|
ca_size_t outer = 1;
|
|
444
620
|
for (int d = 0; d < axis; d++)
|
|
@@ -450,12 +626,12 @@ rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
|
|
|
450
626
|
|
|
451
627
|
ca_size_t chunk_bytes = inner * ca->bytes;
|
|
452
628
|
ca_size_t stride = n * chunk_bytes;
|
|
453
|
-
char *tmp = (char
|
|
629
|
+
char *tmp = ALLOCV_N(char, tmp_holder, chunk_bytes);
|
|
454
630
|
|
|
455
631
|
for (ca_size_t o = 0; o < outer; o++) {
|
|
456
632
|
char *base = ca->ptr + o * stride;
|
|
457
633
|
for (ca_size_t i = n - 1; i > 0; i--) {
|
|
458
|
-
unsigned long j = ca_random_ulong_limited(
|
|
634
|
+
unsigned long j = ca_random_ulong_limited(&c->source, (unsigned long)i);
|
|
459
635
|
if ((ca_size_t)j != i) {
|
|
460
636
|
swap_chunks(base + i * chunk_bytes,
|
|
461
637
|
base + j * chunk_bytes,
|
|
@@ -463,11 +639,38 @@ rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
|
|
|
463
639
|
}
|
|
464
640
|
}
|
|
465
641
|
}
|
|
466
|
-
|
|
642
|
+
ALLOCV_END(tmp_holder);
|
|
643
|
+
}
|
|
644
|
+
return Qnil;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
static VALUE
|
|
648
|
+
rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
|
|
649
|
+
{
|
|
650
|
+
CArray *ca;
|
|
651
|
+
VALUE opts = Qnil, v_axis = Qnil;
|
|
652
|
+
int axis = -1;
|
|
653
|
+
ca_random_ctx_t c;
|
|
654
|
+
|
|
655
|
+
rb_scan_args(argc, argv, "0:", &opts);
|
|
656
|
+
VALUE rng = Qnil;
|
|
657
|
+
rb_scan_options(opts, "rng,axis", &rng, &v_axis);
|
|
658
|
+
|
|
659
|
+
rb_ca_modify(self);
|
|
660
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
661
|
+
|
|
662
|
+
if (ca->elements <= 1) return self;
|
|
663
|
+
|
|
664
|
+
if (!NIL_P(v_axis)) {
|
|
665
|
+
axis = rb_ca_normalize_axis_for_ndim(ca_axis_integer(v_axis, NULL), ca->ndim, NULL);
|
|
666
|
+
if (ca->dim[axis] <= 1) {
|
|
667
|
+
return self;
|
|
668
|
+
}
|
|
467
669
|
}
|
|
468
670
|
|
|
469
|
-
|
|
470
|
-
|
|
671
|
+
c.ca = ca;
|
|
672
|
+
c.axis = axis;
|
|
673
|
+
ca_random_run(&c, rng, ca_shuffle_bang_fill);
|
|
471
674
|
return self;
|
|
472
675
|
}
|
|
473
676
|
|
|
@@ -503,11 +706,186 @@ rb_ca_randomn(int argc, VALUE *argv, VALUE self)
|
|
|
503
706
|
return rb_ca_randomn_bang(argc, argv, copy);
|
|
504
707
|
}
|
|
505
708
|
|
|
709
|
+
/* ---- CArray::Rng ---------------------------------------------------- */
|
|
710
|
+
|
|
711
|
+
/* A generator with its own state, so that a sequence belongs to an object
|
|
712
|
+
* rather than to the process:
|
|
713
|
+
*
|
|
714
|
+
* r = CArray::Rng.new(seed: 4)
|
|
715
|
+
* a.random!(rng: r) # fills, advancing r
|
|
716
|
+
* CArray.jit_for(n) { |i| b[i] = r.call } # carries on from there
|
|
717
|
+
*
|
|
718
|
+
* The state is an ordinary CA_INT64 array of four cells, which is what
|
|
719
|
+
* lets the second line work: carray-jit hands that array's address to a
|
|
720
|
+
* kernel that pasted the same generator, and the kernel advances the
|
|
721
|
+
* same cells this file does. Nothing about the generator is hidden
|
|
722
|
+
* behind a struct only this extension can read.
|
|
723
|
+
*
|
|
724
|
+
* The seed is not part of the state. It is remembered so that #reset
|
|
725
|
+
* with no argument can repeat a run, and so `inspect` can say what a
|
|
726
|
+
* generator was started from. */
|
|
727
|
+
|
|
728
|
+
static VALUE rb_ca_rng_reset (int argc, VALUE *argv, VALUE self);
|
|
729
|
+
|
|
730
|
+
/* The state's cells, attached. Every entry point here goes through this
|
|
731
|
+
* rather than reaching into the ivar, so that "what is the state" has one
|
|
732
|
+
* answer even after another generator is added. */
|
|
733
|
+
static CArray *
|
|
734
|
+
ca_rng_cells (VALUE self, int64_t **cells)
|
|
735
|
+
{
|
|
736
|
+
CArray *ca;
|
|
737
|
+
VALUE state = rb_ivar_get(self, rb_intern("@state"));
|
|
738
|
+
TypedData_Get_Struct(state, CArray, &carray_data_type, ca);
|
|
739
|
+
ca_attach(ca); /* window: the state is an entity, whose attach holds nothing */
|
|
740
|
+
*cells = (int64_t *) ca->ptr;
|
|
741
|
+
return ca;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/* CArray::Rng.new(generator = :xoshiro256pp, seed: nil) */
|
|
745
|
+
static VALUE
|
|
746
|
+
rb_ca_rng_initialize (int argc, VALUE *argv, VALUE self)
|
|
747
|
+
{
|
|
748
|
+
VALUE gen = Qnil, opts = Qnil, seed = Qnil, state;
|
|
749
|
+
|
|
750
|
+
rb_scan_args(argc, argv, "01:", &gen, &opts);
|
|
751
|
+
rb_scan_options(opts, "seed", &seed);
|
|
752
|
+
|
|
753
|
+
if (NIL_P(gen)) gen = ID2SYM(rb_intern("xoshiro256pp"));
|
|
754
|
+
if (!SYMBOL_P(gen) || SYM2ID(gen) != rb_intern("xoshiro256pp")) {
|
|
755
|
+
rb_raise(rb_eArgError,
|
|
756
|
+
"unknown generator %"PRIsVALUE"; carray has :xoshiro256pp",
|
|
757
|
+
rb_inspect(gen));
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
state = rb_funcall(rb_cCArray, rb_intern("int64"), 1, INT2FIX(4));
|
|
761
|
+
rb_ivar_set(self, rb_intern("@generator"), gen);
|
|
762
|
+
rb_ivar_set(self, rb_intern("@state"), state);
|
|
763
|
+
|
|
764
|
+
return rb_ca_rng_reset(NIL_P(seed) ? 0 : 1, &seed, self);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/* CArray::Rng#reset(seed = nil) — start the sequence over.
|
|
768
|
+
*
|
|
769
|
+
* With no argument, from the seed this generator already carries, which
|
|
770
|
+
* repeats the run exactly. A generator made without a seed is given one
|
|
771
|
+
* from Random.new_seed, so two of them differ; that drawn seed is kept,
|
|
772
|
+
* so even an unseeded run can be repeated once it has begun. */
|
|
773
|
+
static VALUE
|
|
774
|
+
rb_ca_rng_reset (int argc, VALUE *argv, VALUE self)
|
|
775
|
+
{
|
|
776
|
+
VALUE seed = Qnil, masked;
|
|
777
|
+
CArray *ca;
|
|
778
|
+
int64_t *cells;
|
|
779
|
+
|
|
780
|
+
rb_scan_args(argc, argv, "01", &seed);
|
|
781
|
+
if (NIL_P(seed)) seed = rb_ivar_get(self, rb_intern("@seed"));
|
|
782
|
+
if (NIL_P(seed)) {
|
|
783
|
+
seed = rb_funcall(rb_path2class("Random"), rb_intern("new_seed"), 0);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/* Any Integer is a seed: a negative one and one wider than a word are
|
|
787
|
+
folded into 64 bits rather than refused, which is what `&` does. */
|
|
788
|
+
masked = rb_funcall(rb_to_int(seed), rb_intern("&"),
|
|
789
|
+
1, ULL2NUM(0xFFFFFFFFFFFFFFFFULL));
|
|
790
|
+
|
|
791
|
+
ca = ca_rng_cells(self, &cells);
|
|
792
|
+
ca_xoshiro256pp_seed(cells, (uint64_t) NUM2ULL(masked));
|
|
793
|
+
ca_sync_detach(ca);
|
|
794
|
+
|
|
795
|
+
rb_ivar_set(self, rb_intern("@seed"), seed);
|
|
796
|
+
return self;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/* CArray::Rng#random — one draw in [0.0, 1.0), the state advanced.
|
|
800
|
+
*
|
|
801
|
+
* Named for `CArray#random!`, which it is the one-cell form of, and to
|
|
802
|
+
* pair with `#randomn` the way those two pair. Not `#rand`: Ruby's
|
|
803
|
+
* `random:` keyword calls `rand(n)` on what it is given, and a `#rand`
|
|
804
|
+
* here that takes no argument would make this look usable there and
|
|
805
|
+
* then fail with an arity error rather than say what it is.
|
|
806
|
+
*
|
|
807
|
+
* It is the same draw `random!` takes for one cell, and the same one a
|
|
808
|
+
* kernel's `random(rng:)` takes, because all three run the code in
|
|
809
|
+
* ca_rng_xoshiro256pp.h. */
|
|
810
|
+
static VALUE
|
|
811
|
+
rb_ca_rng_random (VALUE self)
|
|
812
|
+
{
|
|
813
|
+
CArray *ca;
|
|
814
|
+
int64_t *cells;
|
|
815
|
+
double value;
|
|
816
|
+
|
|
817
|
+
ca = ca_rng_cells(self, &cells);
|
|
818
|
+
value = ca_xoshiro256pp_next_real(cells);
|
|
819
|
+
ca_sync_detach(ca);
|
|
820
|
+
return rb_float_new(value);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/* CArray::Rng#bits — the same draw as the generator's raw 64 bits.
|
|
824
|
+
*
|
|
825
|
+
* Not a bounded draw and not Ruby's `rand(n)`: it is the word the
|
|
826
|
+
* generator produced, before it was turned into a double. What that is
|
|
827
|
+
* for is checking this generator against the sequence its authors
|
|
828
|
+
* published, which is the one question `#rand` cannot answer. */
|
|
829
|
+
static VALUE
|
|
830
|
+
rb_ca_rng_bits (VALUE self)
|
|
831
|
+
{
|
|
832
|
+
CArray *ca;
|
|
833
|
+
int64_t *cells;
|
|
834
|
+
uint64_t value;
|
|
835
|
+
|
|
836
|
+
ca = ca_rng_cells(self, &cells);
|
|
837
|
+
value = ca_xoshiro256pp_next(cells);
|
|
838
|
+
ca_sync_detach(ca);
|
|
839
|
+
return ULL2NUM(value);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/* CArray::Rng#randomn — one standard normal, which is two draws.
|
|
843
|
+
*
|
|
844
|
+
* The same one `randomn!(rng: self)` writes into a cell and the same
|
|
845
|
+
* one a kernel's `randomn(rng: self)` takes, because all three are
|
|
846
|
+
* ca_random_normal. */
|
|
847
|
+
static VALUE
|
|
848
|
+
rb_ca_rng_randomn (VALUE self)
|
|
849
|
+
{
|
|
850
|
+
CArray *ca;
|
|
851
|
+
int64_t *cells;
|
|
852
|
+
ca_rng_t source;
|
|
853
|
+
double value;
|
|
854
|
+
|
|
855
|
+
ca = ca_rng_cells(self, &cells);
|
|
856
|
+
source.kind = CA_RNG_OWN;
|
|
857
|
+
source.rng = self;
|
|
858
|
+
source.state = NULL;
|
|
859
|
+
source.cells = cells;
|
|
860
|
+
value = ca_random_normal(&source);
|
|
861
|
+
ca_sync_detach(ca);
|
|
862
|
+
return rb_float_new(value);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
static VALUE
|
|
866
|
+
rb_ca_rng_inspect (VALUE self)
|
|
867
|
+
{
|
|
868
|
+
return rb_sprintf("#<CArray::Rng %"PRIsVALUE" seed=%"PRIsVALUE">",
|
|
869
|
+
rb_ivar_get(self, rb_intern("@generator")),
|
|
870
|
+
rb_ivar_get(self, rb_intern("@seed")));
|
|
871
|
+
}
|
|
872
|
+
|
|
506
873
|
/* ---- Init -------------------------------------------------------------- */
|
|
507
874
|
|
|
508
875
|
void
|
|
509
876
|
Init_carray_random (void)
|
|
510
877
|
{
|
|
878
|
+
rb_cCARng = rb_define_class_under(rb_cCArray, "Rng", rb_cObject);
|
|
879
|
+
rb_define_method(rb_cCARng, "initialize", rb_ca_rng_initialize, -1);
|
|
880
|
+
rb_define_method(rb_cCARng, "reset", rb_ca_rng_reset, -1);
|
|
881
|
+
rb_define_method(rb_cCARng, "random", rb_ca_rng_random, 0);
|
|
882
|
+
rb_define_method(rb_cCARng, "randomn", rb_ca_rng_randomn, 0);
|
|
883
|
+
rb_define_method(rb_cCARng, "bits", rb_ca_rng_bits, 0);
|
|
884
|
+
rb_define_method(rb_cCARng, "inspect", rb_ca_rng_inspect, 0);
|
|
885
|
+
rb_define_attr(rb_cCARng, "generator", 1, 0);
|
|
886
|
+
rb_define_attr(rb_cCARng, "state", 1, 0);
|
|
887
|
+
rb_define_attr(rb_cCARng, "seed", 1, 0);
|
|
888
|
+
|
|
511
889
|
rb_define_method(rb_cCArray, "random!", rb_ca_random_bang, -1);
|
|
512
890
|
rb_define_method(rb_cCArray, "randomn!", rb_ca_randomn_bang, -1);
|
|
513
891
|
rb_define_method(rb_cCArray, "shuffle!", rb_ca_shuffle_bang, -1);
|