Skip to content

Commit 17050be

Browse files
committed
bug in sparse i. when no indexes
1 parent 4ef1059 commit 17050be

5 files changed

Lines changed: 71 additions & 23 deletions

File tree

jsrc/vfrom.c

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,8 @@ struct __attribute__((aligned(CACHELINESIZE))) mvmctx {
672672
A ndxa; // the indexes, always a boxed list of arrays
673673
// obsolete I nc; I *ndx; I *ndx0; I *ndxe;
674674
I n; // #rows/cols in M
675-
D *bv;
675+
D *bv; // pointer to bk. If 0, this is either a column extraction (if zv!=0) or a Dpiv operation (if zv=0)
676+
// 0 1 2 3
676677
__m256d thresh; // ColThr Inf bkmin MinPivot validity thresholds, small positive values
677678
// obsolete __m256d oldcol; // oldcol Frow 0 0 col value at previous smallest pivot / Frow of current col (always <=0) 0 0
678679
// obsolete __m256d oldbk; // oldbk minimp 0 0 bk value at previous smallest pivot, best gain available in a previous col (always <=0) 0 0
@@ -701,7 +702,7 @@ static unsigned char jtmvmsparsex(J jt,void* const ctx,UI4 ti){
701702
// perform the operation
702703

703704
#define PROCESSROWRATIOS /* We run this after the dot-product has been loaded into dotprod */ \
704-
if(likely(bv!=0)){ \
705+
if(bv!=0){ \
705706
/* DIP processing. col data is in dotprod as col col col col */ \
706707
if(likely(exlist==0)){ /* normal look for improving pivot */ \
707708
__m256d ratios=_mm256_mul_pd(oldbk,dotprod); /* ratios is col*oldbk col*minimp 0 0 */ \
@@ -733,6 +734,7 @@ static unsigned char jtmvmsparsex(J jt,void* const ctx,UI4 ti){
733734
}else if(c>0)goto abortcol; /* possible dangerous pivot: skip the column */ \
734735
} \
735736
} \
737+
}else if(zv==0){bestrow+=_mm256_cvtsd_f64(thresh)<=_mm256_cvtsd_f64(dotprod); /* counting eligibles for Dpiv */ \
736738
}else{zv[i]=_mm256_cvtsd_f64(dotprod); /* just fetching the column: do it */ \
737739
}
738740
__m256d oldcol; // oldcol Frow 0 0 col value at previous smallest pivot / Frow of current col (always <=0) 0 0
@@ -761,15 +763,15 @@ static unsigned char jtmvmsparsex(J jt,void* const ctx,UI4 ti){
761763
#define COLLPE }while(++bvgrd!=bvgrde);
762764
do{
763765
I colx=*ndx; // get next column# to work on
764-
I bestrow; // the best row to use as a pivot for this column
766+
I bestrow; // the best row to use as a pivot for this column; or # qualifying Dpiv found.
765767
if(likely(bv!=0)){
766768
if(exlist==0){
767769
// col init
768770
oldcol=_mm256_set_pd(0.0,0.0,Frow[colx],0.0); // init to pivotratio=inf and gain 0, assuming minimp is 0 the first time
769771
oldbk=_mm256_set_pd(0.0,0.0,minimp,1.0);
770772
}
771773
bestrow=-1; // init no eligible row found
772-
}
774+
}else bestrow=0; // for Dpiv, init to none found
773775
COLLPINIT
774776
// if the column is just to be fetched from M, do so without dot-product. We can use gather down the column, but there's no gain
775777
__m256d dotprod; // place where product is assembled or read into
@@ -868,8 +870,11 @@ static unsigned char jtmvmsparsex(J jt,void* const ctx,UI4 ti){
868870
} // end 'long product'
869871
} // end 'needed dot-product'
870872
// done with one column. Collect stats and update parms for the next column
871-
if(unlikely(!bv))break; // if just one product, skip the setup for next column
872-
if(exlist==0){ // looking for nonimproving pivots?
873+
if(bv==0){ // one product, or Dpiv
874+
if(zv)break; // if just one product, skip the setup for next column
875+
// here for Dpiv. bestrow+1 is the # pivots found; we add/sub/store that in the Dpiv block based on 'prirow'
876+
exlist[colx]=exlist[colx]*!!prirow+bestrow*(prirow|1); // add/sub/init Dpiv value. Only one thread ever touches a column
877+
}else if(exlist==0){ // looking for nonimproving pivots?
873878
// not looking for nonimproving pivots. Do a normal pivot-pick
874879
// column ran to completion. Detect unbounded
875880
if(bestrow<0){bestcolrow=bestrow; bestcol=*ndx; goto return4;} // no pivots found for a column, problem is unbounded, indicate which column in the NTT, i. e. the input value which is an identity column if < #A
@@ -892,7 +897,7 @@ static unsigned char jtmvmsparsex(J jt,void* const ctx,UI4 ti){
892897
}
893898
}
894899
--bvgrd; // undo the +1 in the product-accounting below
895-
abortcol: // here is column aborted early, possibly on insufficient gain
900+
abortcol: // here if column aborted early, possibly on insufficient gain
896901
if(unlikely(!bv))break; // if just one product, skip the setup for next column
897902
ndotprods+=bvgrd-bvgrd0+1; // accumulate # products performed, including the one we aborted out of
898903
if(unlikely((--ncols<0)&&(SGNIF(bestcolrow,32+3)<0))){++ndx; break;} // quit if we have made a non-dangerous improvement AND have processed enough columns - include the aborted column in the count
@@ -911,6 +916,7 @@ static unsigned char jtmvmsparsex(J jt,void* const ctx,UI4 ti){
911916
}
912917
}
913918
}while(++ndx!=ndxe); // end loop over columns
919+
if(bv==0)R 0; // if Dpiv or single column, ctx is unused for return
914920
// operation complete; transfer results back to ctx. To reduce stores we jam the col/row together
915921
__atomic_fetch_add(&((struct mvmctx*)ctx)->ndotprods,ndotprods,__ATOMIC_ACQ_REL); // accumulate stats for the work done here: dot-products
916922
__atomic_fetch_add(&((struct mvmctx*)ctx)->ncolsproc,ndx-ndx0,__ATOMIC_ACQ_REL); // ...and # columns inspected
@@ -950,14 +956,14 @@ static unsigned char jtmvmsparsex(J jt,void* const ctx,UI4 ti){
950956
// if ndx<m, the column is ndx {"1 M; otherwise ((((ndx-m){Ax) ];.0 Am) {"1 M) +/@:*"1 ((ndx-m){Ax) ];.0 Av
951957
// Result for product mode (exitvec is scalar) is the product
952958
// DIP mode
953-
// y is ndx;Ax;Am;Av;(M, shape m,n);bkgrd;(ColThreshold,MinPivot,bkmin,NFreeCols,NCols,ImpFac,Nvirt);bk;Frow[;exclusion list;Yk]
959+
// y is ndx;Ax;Am;Av;(M, shape m,n);bkgrd;(ColThreshold/PivTol,MinPivot,bkmin,NFreeCols,NCols,ImpFac,Virtx/Dpivdir);bk/'';Frow[;exclusion list/Dpiv;Yk]
954960
// Result is rc,best row,best col,#cols scanned,#dot-products evaluated,best gain (if rc e. 0 1 2)
955961
// rc,failing column of NTT, an element of ndx (if rc=4)
956962
// rc=0 is good; rc=1 means the pivot found is dangerously small; rc=2 nonimproving pivot found; rc=3 no pivot found, stall; rc=4 means the problem is unbounded (only the failing column follows)
957963
// rc=5 (not created - means problem is infeasible) rc=6=empty M, problem is malformed
958964
// if the exclusion list is given, we stop on the first nonimproving pivot, and the exclusion list is used to prevent repetition of basis
959965
// If Frow is empty, we are looking for nonimproving pivots in rows where the selector is 0. In that case the bkgrd puts the bk values in descending order. We return the first column that will make more 0 B rows non0 than non0 B rows 0.
960-
//
966+
// If bk is empty, we are counting the #places where c>=PivTol and accumulating into Dpiv under control of Dpivdir (-1=decr, 1=incr; init to 0 if neg)
961967
// Rank is infinite
962968
F1(jtmvmsparse){PROLOG(832);
963969
#if C_AVX2
@@ -1009,32 +1015,45 @@ F1(jtmvmsparse){PROLOG(832);
10091015
D bkmin; // the largest value for which bk is considered close enough to 0
10101016

10111017
if(AR(C(AAV(w)[0]))==0){
1012-
// single index value. set bv=0 as a flag that we are storing the column
1018+
// single index value. set bv=0, zv non0 as a flag that we are storing the column
10131019
bv=0; ASSERT(AN(w)==5,EVLENGTH); // if goodvec is an atom, set bv=0 to indicate that bv is not used and verify no more input
10141020
if(unlikely(n==0)){R reshape(sc(n),zeroionei(0));} // empty M, each product is 0
10151021
GATV0(z,FL,n,1); zv=DAV(z); // allocate the result area for column extraction. Set zv nonzero so we use bkgrd of i. #M
10161022
bvgrd0=0; bvgrde=bvgrd0+AS(C(AAV(w)[4]))[0]; // length of column is #M
10171023
}else{
1018-
// A list of index values. We are doing the DIP calculation
1024+
// A list of index values. We are doing the DIP calculation or Dpiv
10191025
ASSERT(AR(C(AAV(w)[5]))==1,EVRANK); ASSERT(AN(C(AAV(w)[5]))==0||AT(C(AAV(w)[5]))&INT,EVDOMAIN); bvgrd0=IAV(C(AAV(w)[5])); bvgrde=bvgrd0+AN(C(AAV(w)[5])); // bkgrd: the order of processing the rows, and end+1 ptr normally /: bk
10201026
if(AN(C(AAV(w)[5]))==0){RETF(num(6))} // empty bk - give error/empty result 6
10211027
ASSERT(BETWEENC(AN(w),8,11),EVLENGTH);
1022-
ASSERT(AR(C(AAV(w)[7]))<=1,EVRANK); ASSERT(AT(C(AAV(w)[7]))&FL,EVDOMAIN); ASSERT(AN(C(AAV(w)[7]))==AS(C(AAV(w)[4]))[0],EVLENGTH); bv=DAV(C(AAV(w)[7])); // bk, one per row of M
10231028
ASSERT(AR(C(AAV(w)[8]))<=1,EVRANK); // Frow, one per row of M and column of A
10241029
if(AN(C(AAV(w)[8]))==0)Frow=0;else{ASSERT(AT(C(AAV(w)[8]))&FL,EVDOMAIN); ASSERT(AN(C(AAV(w)[8]))==AS(C(AAV(w)[4]))[0]+AS(C(AAV(w)[1]))[0],EVLENGTH); Frow=DAV(C(AAV(w)[8]));} // if Frow omitted we are looking to make bks nonzero
10251030
ASSERT(AR(C(AAV(w)[6]))<=1,EVRANK); ASSERT(AT(C(AAV(w)[6]))&FL,EVDOMAIN); ASSERT(AN(C(AAV(w)[6]))==7,EVLENGTH); // 7 float constants
10261031
if(unlikely(n==0)){RETF(num(6))} // empty M - should not occur, give error result 6
10271032
bkmin=DAV(C(AAV(w)[6]))[2];
10281033
thresh=_mm256_set_pd(DAV(C(AAV(w)[6]))[1],bkmin,inf,DAV(C(AAV(w)[6]))[0]); nfreecolsd=(DAV(C(AAV(w)[6]))[3]); ncolsd=(DAV(C(AAV(w)[6]))[4]); impfac=DAV(C(AAV(w)[6]))[5]; prirow=(I)DAV(C(AAV(w)[6]))[6];
1029-
zv=AN(C(AAV(w)[5]))==AN(C(AAV(w)[7]))?Frow:0; // set zv nonzero as a flag to process leading columns in order, until we have an improvement to shoot at. Do this only if ALL values in bk are to be processed
1030-
if(AN(w)>9){
1031-
ASSERT(AN(w)==11,EVLENGTH);
1032-
// An exclusion list is given (and thus also yk). Remember their addresses. Its presence puts us through the 'nonimproving path' case
1034+
ASSERT(AR(C(AAV(w)[7]))<=1,EVRANK);
1035+
if(AN(C(AAV(w)[7]))!=0){
1036+
// Normal DIP calculation
1037+
ASSERT(AT(C(AAV(w)[7]))&FL,EVDOMAIN); ASSERT(AN(C(AAV(w)[7]))==AS(C(AAV(w)[4]))[0],EVLENGTH); bv=DAV(C(AAV(w)[7])); // bk, one per row of M
1038+
zv=AN(C(AAV(w)[5]))==AN(C(AAV(w)[7]))?Frow:0; // set zv nonzero as a flag to process leading columns in order, until we have an improvement to shoot at. Do this only if ALL values in bk are to be processed
1039+
if(AN(w)>9){
1040+
// nonimproving pivots with exlist
1041+
ASSERT(AN(w)==11,EVLENGTH);
1042+
// An exclusion list is given (and thus also yk). Remember their addresses. Its presence puts us through the 'nonimproving path' case
1043+
exlist=IAV(C(AAV(w)[9])); // remember address of exclusions
1044+
nexlist=AN(C(AAV(w)[9])); // and length of list
1045+
ASSERT(AR(C(AAV(w)[9]))<=1,EVRANK); ASSERT(nexlist==0||ISDENSETYPE(AT(C(AAV(w)[9])),INT),EVDOMAIN); // must be integer list
1046+
ASSERT(AR(C(AAV(w)[10]))<=1,EVRANK); ASSERT(ISDENSETYPE(AT(C(AAV(w)[10])),INT),EVDOMAIN); ASSERT(AN(C(AAV(w)[10]))==AS(C(AAV(w)[4]))[0],EVLENGTH); // yk, one per row of M
1047+
yk=IAV(C(AAV(w)[10])); // remember address of translation table of row# to basis column#
1048+
}
1049+
}else{
1050+
// Dpiv counting, with Dpiv. exlist is the Dpiv area
1051+
ASSERT(AN(w)==10,EVLENGTH);
1052+
bv=zv=0;
10331053
exlist=IAV(C(AAV(w)[9])); // remember address of exclusions
1034-
nexlist=AN(C(AAV(w)[9])); // and length of list
1035-
ASSERT(AR(C(AAV(w)[9]))<=1,EVRANK); ASSERT(nexlist==0||ISDENSETYPE(AT(C(AAV(w)[9])),INT),EVDOMAIN); // must be integer list
1036-
ASSERT(AR(C(AAV(w)[10]))<=1,EVRANK); ASSERT(ISDENSETYPE(AT(C(AAV(w)[10])),INT),EVDOMAIN); ASSERT(AN(C(AAV(w)[10]))==AS(C(AAV(w)[4]))[0],EVLENGTH); // yk, one per row of M
1037-
yk=IAV(C(AAV(w)[10])); // remember address of translation table of row# to basis column#
1054+
ASSERT(AR(C(AAV(w)[9]))==1,EVRANK); ASSERT(ISDENSETYPE(AT(C(AAV(w)[9])),INT),EVDOMAIN); // must be integer list
1055+
ASSERT(AN(C(AAV(w)[9]))==AS(C(AAV(w)[2]))[0]+AS(C(AAV(w)[4]))[0],EVLENGTH); // length of Dpiv is #cols of A + #rows of A (=M)
1056+
z=mtv; // no error is possible; use harmless return value
10381057
}
10391058
}
10401059

jsrc/visp.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,17 @@ A jtiovxs(J jt,I mode,A a,A w){A e,x,z;B h;I at,t,wt;P*ap=0,*wp,*zp;
5252

5353
A jtiovsd(J jt,I mode,A a,A w){A ae,ax,ay,p,z;B h,*pv;I at,j,m,n,t,wt,*v,*yv;P*ap;
5454
ap=PAV(a); ax=SPA(ap,x); ay=SPA(ap,i);
55-
if(!AN(ay))R indexofsub(mode,ravel(ax),w);
55+
// obsolete A zz=0;
56+
// obsolete if(!AN(ay))zz= indexofsub(mode,ravel(ax),w);// fails when length not 0
57+
if(!AS(ay)[1])R indexofsub(mode,ravel(ax),w);// special case if no sparse axes: just like dense i.
5658
m=AN(ax); n=AS(a)[0]; yv=AV(ay); ae=SPA(ap,e);
5759
at=DTYPE(AT(a)); wt=AT(w); if(h=HOMO(at,wt))t=maxtype(at,wt);
5860
if(h&&TYPESNE(t,wt))RZ(w=cvt(t,w));
5961
j=ioev(mode,a);
60-
RZ(z=indexofsub(mode,ax,w)); v=AV(z);
62+
RZ(z=mkwris(indexofsub(mode,ax,w))); v=AV(z); // ensure z writable since we may modify it here
6163
RZ(p=eq(ae,w)); pv=BAV(p);
6264
DO(AN(w), *v=pv[i]?j:m>*v?yv[*v]:n; ++v;);
65+
// obsolete if(zz&&!BAV(match(zz,z))[0])SEGFAULT; // scaf
6366
R z;
6467
} /* (sparse vector) i. dense */
6568

jsrc/vs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ static A jtvaxis(J jt,I r,A a){A y;B*b;I j,n,*v;
5555
ASSERT(1>=AR(a),EVRANK);
5656
GATV0(y,B01,r,1); b=BAV(y); mvc(r,b,1,MEMSET00);
5757
DO(n, j=v[i]; if(0>j)j+=r; ASSERT(0<=j&&j<r&&!b[j],EVINDEX); b[j]=1;);
58-
R caro(ifb(r,b)); // avoid readonly
58+
R mkwris(ifb(r,b)); // ensure result writable
5959
} /* standardize axes to be non-negative, sorted */
6060

6161
A jtdaxis(J jt,I r,A a){R less(IX(r),a);}

test/g520.ijs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ assert. 2 0 2 0 4 1 -: (128!:9) 0 1 2 3;(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;bkg
128128
assert. 2 0 2 3 11 1 -: (128!:9) 1 2 3 0;(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;bkg;1e_11 1e_6 1e_11 0 1 1.0 _1;bk;Frow;($00);(4 5 6 7) NB. 2 non0, 2 0
129129
assert. 3 0 0 4 11 0 -: (128!:9) 1 2 3 0;(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;bkg;1e_11 1e_6 1e_11 0 1 1.0 _1;bk;Frow;(,16b000000006);(4 5 6 7) NB. Pivot excluded
130130

131+
NB. Dpiv
132+
M=. |: _4 ]\ _1. 0 2 3 0 2 3 4 2 3 4 5 3 4 5 6 NB.
133+
Dpiv=. (#M) $ _1
134+
assert. '' -: 128!:9 (0 1 2);(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;0 1 2;1.0 0 0 0 0 0 0;'';'';Dpiv NB. init
135+
assert Dpiv -: 1 2 3 _1
136+
assert. '' -: 128!:9 (1 2 3);(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;1 2 3;2.5 0 0 0 0 0 1;'';'';Dpiv NB. add
137+
assert Dpiv -: 1 4 6 2
138+
assert. '' -: 128!:9 (0 1 2 3);(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;0 3;2.5 0 0 0 0 0 _1;'';'';Dpiv NB. add
139+
assert Dpiv -: 0 3 5 0
140+
131141
NB. Repeat multithreaded
132142
bxr =. </.~ 0 1 $~ $ NB. 2 threads
133143
mt3 =. -:&(3&{.)
@@ -211,6 +221,17 @@ assert. 2 0 2 0 4 1 mt3 128!:9 (bxr 0 1 2 3);(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);
211221
assert. 2 0 2 3 11 1 mt3 128!:9 (bxr 1 2 3 0);(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;bkg;1e_11 1e_6 1e_11 0 1 1.0 _1;bk;Frow;($00);(4 5 6 7) NB. 2 non0, 2 0
212222
assert. 3 0 0 4 11 0 mt3 128!:9 (bxr 1 2 3 0);(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;bkg;1e_11 1e_6 1e_11 0 1 1.0 _1;bk;Frow;(,16b000000006);(4 5 6 7) NB. Pivot excluded
213223

224+
NB. Dpiv
225+
M=. |: _4 ]\ _1. 0 2 3 0 2 3 4 2 3 4 5 3 4 5 6 NB.
226+
Dpiv=. (#M) $ _1
227+
assert. '' -: 128!:9 (bxr 0 1 2);(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;0 1 2;1.0 0 0 0 0 0 0;'';'';Dpiv NB. init
228+
assert Dpiv -: 1 2 3 _1
229+
assert. '' -: 128!:9 (bxr 1 2 3);(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;1 2 3;2.5 0 0 0 0 0 1;'';'';Dpiv NB. add
230+
assert Dpiv -: 1 4 6 2
231+
assert. '' -: 128!:9 (bxr 0 1 2 3);(,."1 (_2) ]\ 00 0);(0$00);(0$0.0);M;0 3;2.5 0 0 0 0 0 _1;'';'';Dpiv NB. add
232+
assert Dpiv -: 0 3 5 0
233+
234+
214235
delth'' NB. make sure we end with an empty system
215236

216237
1

test/gspi.ijs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ d=: df s=: 0(2)}$. i.5
167167
(s i. 0) -: d i. 0
168168
(s i: 0) -: d i: 0
169169

170+
s =. $.,1
171+
s =. 0 (0}) s
172+
1 = s i. 1
173+
1 = (8 $. s) i. 1
174+
170175

171176
NB. i. and i: on general dense or sparse arguments ----------------------
172177

0 commit comments

Comments
 (0)