Changelog
Releases from 3.0.0 onward are recorded here. There is no separate NEWS file: this is where to look for what changed between the version you have and a newer one. The 1.x history, up to the 2.0.0 release, is in CHANGELOG.v1.md; 2.0.1 went unrecorded.
3.0.1
-
New:
CArray.jit_for,CArray.jit_eachandCArray.jit_mapname a block that is compiled rather than run. Compiling needs the carray-jit gem; without it they raiseNotImplementedError. An expression over whole arrays wantsCArray.fuse, which needs no compiler. -
New: every iterator answers
accumulatebesidesum; some of them did not. It is the same fold kept in the source's own data type, wheresumanswers in the type the core promotes to -- a window or tile count overuint8cells stays one byte wide. -
New:
CArray::CoreExtensionsadds postfix math onComplex, soa[0].tanhreads the waya.tanhdoes. It covers the seventeen functions a complex array supports and agrees with the array form exactly, branch cuts and the sign of a zero included. Opt in withusing CArray::CoreExtensions. -
New:
ca_is_stride_family(ca)incarray.h, for a C extension that folds a view intoroot->ptr + base + sum(idx[k] * strides[k])itself. True for CAStride, CARefer, CABlock, CARepeat, CATranspose, CAFarray and CAField and the mask array of each, and for an externally installed view that shares their operation table. -
New:
divmodreturns[quotient, remainder]element-wise with the quotient floored, the pair Ruby'sInteger#divmodandFloat#divmodreturn. The quotient keeps the receiver's data type. -
New: two optional CAObject callbacks take a partial fill as a region instead of one
store_addrper cell:fill_block(starts, counts, steps, val)for a forward per-axis sub-region,fill_addrs(addrs, val)otherwise. Defining neither keeps the old behaviour. Filling a 1000x1000 region of a 2000x2000 CAObject: 116 ms to 0.6 ms. -
New:
CAFrame#to_tablerenders the frame as an aligned text table, andinspect/to_ssit on it:p dfsummarises the first 8 and last 2 rows,puts dfprints the whole frame.rows:caps the printed rows,precision:rounds float cells for display (default 6), masked cells show as_. -
New:
CAFrame#to_timetakes aCATime::Grid, positionally or asunit:, so a netCDFunitsattribute goes in whole. The grid also carries an epoch phase theunit:/epoch:pair cannot -- that pair reads the epoch on the coarse grid and loses the time of day. The keyword form is unchanged. -
New:
CATime::Gridpackages the(unit:, origin:)pair that#timesteps,#snapand.from_timestepstake, so it is built once and passed as one value:t.snap(g, direction: :floor),t.timesteps(g),g.at(k). It parses and prints the udunits"<unit> since <instant>"form, holding a phased origin that the keyword pair cannot ("12 hours since 2017-11-30 09:00").CATime#gridis the grid an array is stored on. Storage is unchanged. -
New:
CATime#snap(grid, direction:)rounds to a tick grid, the shapeCArray#snaphas for numbers.#floor/#ceil/#roundare its fixed-direction forms; their results and keyword forms are unchanged. -
New: a time element is taken directly as a start or origin literal, so a
floor/ceil/to_unitanswer feeds back intoCArray.time,time_range,time_series,CArray#timeand anorigin:; it used to have to go out throughDateTimeand be re-parsed. A:Mor:Yelement names the first midnight of its granule. What an origin must satisfy is unchanged. -
New:
CArray.timeparses a year-month ("2019-09") and a bare year ("2019"), so the form a:M/:Yelement prints reads back in. A missing finer field names the head of that period. -
Change:
abs,abs2andargon acmplx64array now returnfloat32instead offloat64-- the width that type carries its real values in, the same onerealandimagalready returned.argon afloat32array likewise returnsfloat32rather than widening.cmplx128andfloat64are unchanged, andargon an integer array still givesfloat64. To keep the old width, add.to_type(:float64). -
Change:
CArray.fusetakes the expression rather than the arrays it is over --CArray.fuse { (a + b) * c }in place ofCArray.fuse(a, b, c) { |x, y, z| (x + y) * z }. What comes back is the expression, sox = CArray.fuse { ... }now wants.to_cafor an array. Where the block's source cannot be read -- anirbprompt, insideeval-- writea.lazy + b.lazy.CArray.lazy(*args) { ... }is gone. -
Change: a lazy expression (
a.lazy + b,CArray.fuse) builds its mask when something reads it, rather than when the expression is built. A mask set on either operand after the expression was built is now seen; before, only the left one was.root_arrayandancestorsnow stop at a lazy operation instead of walking into its left operand. Building a long masked expression is also no longer quadratic in the length of the chain. -
Change: the top-level constant
CA_NILis nowCArray::UNSPECIFIED(CA_UNSPECIFIEDin C). It is an internal sentinel for "the caller gave no argument", not a value to pass in;unmask,shift(fill_value:)andwindow(fill_value:)behave as before. -
Change:
group_by_categoryreductions answer in the data type the core reduction promotes the value to, instead of one chosen per reduction.sumon an integer value now answers in float64 (accumulateis the spelling that stays in the value's type), andmean,varianceandmedianon an object value stay exact.sumon a boolean value andprodormeanon a complex one, which raised, now work. -
Change: the
:*unbound repeat is retired.a[:*, nil]raisesIndexError;CArray#unbound_repeat,CAUnboundRepeatandinsert_axis(repeat: :*)are gone. Use:_, which gives the same shape and now stretches on a store as well as in an operation.CArray#broadcast_toandCArray.meshgrid(sparse: true)are unaffected. -
Change: a binary operation requires shapes to agree, or to differ only in size-1 axes at equal ndim.
(3,2) + (2,3)and(3,2) + (6)raiseArgumentErrorwhere they used to answer in one operand's shape; flatten both sides to combine the values in the order they lie. Comparisons,fmaand the lazy forms follow the same rule. Scalars are unaffected, but a one-element 1-D array such asCArray.int32(1)counts as a shape. -
Change: an assignment requires the shapes to match.
t[] = srcwith a differently shaped source raisesRuntimeError; usesrc.flatten. In exchange a smaller source is repeated to fit, sot[] = row[:_, nil, nil]andt[] = col(shape(n,1)) work where they used to raise. A 1-D side on either end still passes, as do shapes differing only in size-1 axes; Ruby Arrays and scalars are unchanged. -
Change:
to_caon a view derived from a lazy marker returns a new entity rather than the view itself --a.lazy.shift(1, 0).to_ca, inside afuseblock or not.copybehaves as before. -
Change:
/and%follow Ruby instead of C. Integer division floors and the remainder carries the sign of the divisor; float%floors too, float/is unchanged. For the old behaviour usefmod, which now takes integers as well as floats. -
Change:
reminderis removed -- it was IEEE 754 remainder on floats but C%on integers. Usefmodfor the truncated remainder. The IEEE form has no replacement. -
Change: the result-type override of
CArray#conditionalandCArray.selectis nowdata_type:, spelled the way the rest of the library spells it. There is no alias:dtype:raisesunknown keyword. -
Change: a calendar-grid
origin:given to the bucket-grid methods of aCATimearray --timesteps,snap,floor,ceil,round,is_righttime-- now has to be a month head (the 1st at 00:00); it used to drop the day and time silently. A:Ytick likewise has to start in January.from_timestepsalready refused an off-grid origin. -
Change: the MemoryView producer emits the format vocabulary
ruby/memory_view.hspecifies rather than PEP 3118's, so below 32 bits it writesc/C/s/Sin place ofb/B/h/H. From 32 bits up the two already agreed, and?,Zf/Zd,T{...}andNsstay PEP 3118, which Ruby has no spelling for. The consumer side already accepted both, so views produced by 3.0.0 still import. -
Change: multiplying and dividing a
cmplx64array (*,/,rcp,rcp_mul) is computed in double and rounded once, so both are correctly rounded; the old route could be off by about 1800 units in the last place. A product that overflows a float but not a double no longer comes back NaN. Division is 4.2-5.3x faster and multiplication 1.3x slower.+,-and everycmplx128operation are unchanged. -
Change: element-wise math on a
float32orcmplx64array is computed at that width rather than widened and rounded back, sosqrt,exp,log, the trigonometric and hyperbolic families,atan2,hypot,absandargare 1.1-3.5x faster there and can move by a bit or two in the last place. Complexlog,power,exp2andexp10are unchanged, as are the rounding, min / max, comparison and variance families and the wider types. -
Change: a rolling
sum,mean,prod,min,max,allorany--a.windows(-1..1).sumand the like -- over a window up to five cells wide on every axis is 2-5x faster, and 1.3-4x over a masked source;min_count:andfill_value:come along.min,maxandprodanswer exactly as before;sumandmeanmay differ in the last bits. A wider window, or any other reduction, is unchanged. -
Change:
CATime#to_unitfloors to a coarser grid instead of raising, and crosses the calendar / fixed-length boundary (:M<->:D) through civil-date algebra.:Y/:M->:Wstill raises. -
Change:
CATimedelta#to_unittruncates toward zero instead of raising on a coarser target. Crossing the calendar boundary still raises. -
Fix:
nextafteron afloat32array returned its own input. The step was taken in double, and the next double above a float rounds back to that same float. It now steps by one float32 ulp. -
Fix: a lazy expression over an object array (
CArray.object,CA_OBJECT) returned wrong values, and crashed when materialised repeatedly. Affectsto_caandcopyon a lazy view, an eager operation with a lazy operand, and a reduction over one. Other data types were never affected. -
Fix: reading a concatenated view (
CArray.concat,CAFrame.concat) backwards along the concatenated axis --m.reverseand any other negative step -- raised IndexError instead of answering. -
Fix: reading a lazy expression (
a.lazy + b,CArray.fuse) with a step other than one --expr[[0, 3, 2]],expr.reverseand the like -- returned values the expression cannot produce, because its operands were read from the wrong cells. Comparisons carried the same fault,a.lazy > band one-operand ones such asa.lazy.is_nanalike. Contiguous reads were never affected. -
Fix:
a.value + braisedcan not create mask array for the value arraywhenbcarried a mask. It now propagatesb's mask. -
Fix: a rolling window that does not cover the cell it is centred on --
windows(1..2), the two cells after this one -- was read as if it started at the array's edge, so every answer came out shifted by the range's start, andbounds: :truncateproduced anchors whose window did not fit.windows(1..2)on[1..8]now sums to[5, 7, 9, 11, 13, 15, 8, 0]. A window that covers its anchor, which is every centred one, is unaffected. -
Fix: storing a
Complexinto acmplx64orcmplx128array kept the sign of a negative zero real part only when the imaginary part was also negative, soComplex(-0.0, 0.0)came back as0.0+0.0i. The sign of a zero picks the side of a branch cut, so a value stored this way could be carried to the wrong branch. All four sign combinations now round-trip, through element assignment and throughto_type. -
Fix:
sinh,cosh,tanh,asinh,acoshandatanhon a complex array gave the hyperbolic function of the real part alone:cmplx128andcmplx64arrays came back withtanh(Re z)wherectanh(z)was meant, wrong in both parts, andacoshandatanhalso returned 0 or infinity where the true value is finite. They now agree with C99complex.h. Real and object arrays were never affected. -
Fix: a C extension reading a view in column-major order -- axes and steps reversed against the view's own -- got wrong values from a view with a length-1 axis, such as
v[nil, :_],v.reshape(n, 1)or a one-column slice: the first cell repeated, a read out of bounds, or a hang.carray-linalg-accelerate'ssolve(a, b)returnedb[0]repeated for a single-column right-hand side. -
Fix: an operation between two views of an array that computes its values -- a lazy expression such as
(a.lazy + b), or aCAObjectover a file -- no longer reads that source one cell at a time.+,fmaand comparisons on such a pair now run at the speed of copying each operand first, so the.copythat worked around it is no longer needed. Single-operand operations, copies, region transfers and reductions were already unaffected. -
Fix: views built inside a
CArray.fuseblock stay part of the expression instead of dropping out of it, so a stencil written the natural way is fused.[],shift,roll,flip/reverse,transpose/T,reshape,flatten,window,diagonal,tileandreferkeep the chain;unbound_repeatand[:*, ...]deliberately do not. -
Fix: a window or shift over a view parent no longer copies that parent in full on every transfer.
a[nil, nil].shift(1, 0)and friends now read at parity with an entity parent and write several times faster. -
Fix: reductions over a bare
.lazymarker no longer raise. Per-axis forms and anything over a masked array failed, soa.lazy.sum(axis: 0)raised whilea.lazy.sumworked. -
Fix:
ca_test_flag/ca_set_flag/ca_unset_flagincarray.h, which only a C extension calls, did not parenthesise their flag argument, so testing two flags at once was true for every array. -
Fix: a Face no longer hands back its storage bytes through the type casts.
as_type,fakeandCArray.wrap_writableraise;CArray.wrap_readonlyconverts asto_typedoes, which also makest.eq(o)ando.eq(t)agree. Reach the storage explicitly witht.parent.fake(...). Numeric Faces are unaffected. -
Fix:
arangeraisedNoMethodErrorin every form; it builds the array now. Integer arguments count exactly, so a step dividing the span evenly no longer picks up an extra element. A zero step or a wrong argument count raisesArgumentError. -
Fix:
from_timestepson a week grid answered Thursdays. The week grid counts from the epoch, which is one, so it cannot hold an ISO Monday head; a week bucket now answers on the day grid and round-trips againstfloorcell for cell. A day-or-finer array keeps the Monday default, a week-stored array its own epoch-anchored ticks. -
Fix: a masked cell decided whether a
CATimeconversion fit. The range guards took their extremes with the mask stripped, soto_unitraisedRangeErrorover a wide value that was masked out. They now answer UNDEF when there is nothing to bound -- an empty array, or every cell masked. -
Fix:
CATimedelta::Element#/floored a negative duration, so-30h / 4answered-8hwhere the array form answered-7h. A duration is a magnitude, so it shrinks toward zero, matching the array form in all four sign combinations. -
Fix:
CArray.timeno longer rolls a field that is out of range over into another date."2019-02-31"parsed to 2019-03-03, and"201909"-- a valid YYMMDD to Ruby, 2020-19-09 -- to 2021-07; both now raise. -
Fix:
CATime#to_unitandCATimedelta#to_unitwere wrong between two resolutions where neither tick is a whole multiple of the other: converting 3 hours to a"90 minutes"grid gave 1 unit rather than 2. -
Fix: importing a MemoryView whose mask is published as
Corcno longer fails. The check knew only PEP 3118'sB,band?, so a producer that spells a byte in Ruby's format vocabulary was refused.
3.0.0
First public release. Earlier versions existed on RubyGems, but the library was developed for the author's own use; 3.0 is where it is packaged, documented and tested as something other people can pick up. It is not source-compatible with 2.0.1.
See README.md for what the library does, and docs/ and guides/ for the reference and the guides.
Requires Ruby 3.0 or later (3.1 for the MemoryView-backed paths).