Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file sampler.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2021 Nomadic Labs, <contact@nomadic-labs.com> *)(* *)(* Permission is hereby granted, free of charge, to any person obtaining a *)(* copy of this software and associated documentation files (the "Software"),*)(* to deal in the Software without restriction, including without limitation *)(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)(* and/or sell copies of the Software, and to permit persons to whom the *)(* Software is furnished to do so, subject to the following conditions: *)(* *)(* The above copyright notice and this permission notice shall be included *)(* in all copies or substantial portions of the Software. *)(* *)(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)(* DEALINGS IN THE SOFTWARE. *)(* *)(*****************************************************************************)(*
This module implements the alias method for sampling from a given
distribution. The distribution need not be normalized.
*)moduletypeSMass=sigtypetvalencoding:tData_encoding.tvalzero:tvalof_int:int->tvalmul:t->t->tvaladd:t->t->tvalsub:t->t->tval(=):t->t->boolval(<=):t->t->boolval(<):t->t->boolendmoduletypeS=sigtypemasstype'atvalcreate:('a*mass)list->'atvalsample:'at->(int_bound:int->mass_bound:mass->int*mass)->'avalencoding:'aData_encoding.t->'atData_encoding.tendmoduleMake(Mass:SMass):Swithtypemass=Mass.t=structtypemass=Mass.ttype'at={total:Mass.t;support:'aFallbackArray.t;p:Mass.tFallbackArray.t;alias:intFallbackArray.t;}letrecinit_looptotalpaliassmalllarge=match(small,large)with|[],_->List.iter(fun(_,i)->FallbackArray.setpitotal)large|_,[]->(* This can only happen because of numerical inaccuracies e.g. when using
[Mass.t = float] *)List.iter(fun(_,i)->FallbackArray.setpitotal)small|(qi,i)::small',(qj,j)::large'->FallbackArray.setpiqi;FallbackArray.setaliasij;letqj'=Mass.sub(Mass.addqiqj)totalinifMass.(qj'<total)theninit_looptotalpalias((qj',j)::small')large'elseinit_looptotalpaliassmall'((qj',j)::large')letsupport:fallback:'a->('a*Mass.t)list->'aFallbackArray.t=fun~fallbackmeasure->FallbackArray.of_list~fallback~proj:fstmeasureletcheck_and_cleanupmeasure=lettotal,measure=List.fold_left(fun((total,m)asacc)((_,p)aspoint)->ifMass.(zero<p)then(Mass.addtotalp,point::m)elseifMass.(p<zero)theninvalid_arg"create"else(* p = zero: drop point *)acc)(Mass.zero,[])measureinmatchmeasurewith|[]->invalid_arg"create"|(fallback,_)::_->(fallback,total,measure)(* NB: duplicate elements in the support are not merged;
the algorithm should still function correctly. *)letcreate(measure:('a*Mass.t)list)=letfallback,total,measure=check_and_cleanupmeasureinletlength=List.lengthmeasureinletn=Mass.of_intlengthinlet_,small,large=List.fold_left(fun(i,small,large)(_,p)->letq=Mass.mulpninifMass.(q<total)then(i+1,(q,i)::small,large)else(i+1,small,(q,i)::large))(0,[],[])measureinletsupport=support~fallbackmeasureinletp=FallbackArray.makelengthMass.zeroinletalias=FallbackArray.makelength(-1)ininit_looptotalpaliassmalllarge;{total;support;p;alias}letsample{total;support;p;alias}draw_i_elt=letn=FallbackArray.lengthsupportinleti,elt=draw_i_elt~int_bound:n~mass_bound:totalinletp=FallbackArray.getpiinifMass.(elt<p)thenFallbackArray.getsupportielseletj=FallbackArray.getaliasiinassert(Compare.Int.(j>=0));FallbackArray.getsupportj(* Note: this could go in the environment maybe? *)letarray_encoding:'aData_encoding.t->'aFallbackArray.tData_encoding.t=funvenc->letopenData_encodinginconv(funarray->letlength=FallbackArray.lengtharrayinletfallback=FallbackArray.fallbackarrayinletelements=List.rev(FallbackArray.fold(funaccelt->elt::acc)array[])in(length,fallback,elements))(fun(length,fallback,elements)->letarray=FallbackArray.makelengthfallbackinList.iteri(funielt->FallbackArray.setarrayielt)elements;array)(obj3(req"length"int31)(req"fallback"venc)(req"elements"(listvenc)))letmass_array_encoding=array_encodingMass.encodingletint_array_encoding=array_encodingData_encoding.int31letencodingenc=letopenData_encodinginconv(fun{total;support;p;alias}->(total,support,p,alias))(fun(total,support,p,alias)->{total;support;p;alias})(obj4(req"total"Mass.encoding)(req"support"(array_encodingenc))(req"p"mass_array_encoding)(req"alias"int_array_encoding))endmoduleInternal_for_tests=structmoduleMake=MakemoduletypeSMass=SMassendmoduleMass:SMasswithtypet=int64=structtypet=int64letencoding=Data_encoding.int64letzero=0Lletof_int=Int64.of_intletmul=Int64.mulletadd=Int64.addletsub=Int64.sublet(=)=Compare.Int64.(=)let(<=)=Compare.Int64.(<=)let(<)=Compare.Int64.(<)end(* This is currently safe to do that since since at this point the values for
[total] is 8 * 10^8 * 10^6 and the delegates [n] = 400.
Therefore [let q = Mass.mul p n ...] in [create] does not overflow since p <
total.
Assuming the total active stake does not increase too much, which is the case
at the current 5% inflation rate, this implementation can thus support around
10000 delegates without overflows.
If/when this happens, the implementation should be revisited.
*)includeMake(Mass)