###################################################################### #CanonicalBF.txt Save this file as SLprog.txt # #To use it, stay in the # ##same directory, get into Maple (by typing: maple ) # ##and then type: read CanonicalBF.txt : # ##Then follow the instructions given there # ## # ##Written by Blair Seidler, Rutgers University , # #blair at math dot rutgers dot edu # ###################################################################### #Created: Sep. 2021 print(`Created: Sep. 2021`): print(`This is CanonicalBF, which finds the canonical`): print(`list of Boolean functions for a given number of`): print(`variables.`): print(``): print(`In this package, we will represent a Boolean function as `): print(`a set of vectors (lists) in {-1,0,1}^n, where -1 is false, `): print(`1 is true, and 0 is don't care.`): print(`{[-1,0,1]} can be interpreted as "not x[1] and x[3]"`): print(`{[-1,0,0],[0,0,1]} can be interpreted as "not x[1] or x[3]"`): print(``): print(`written by Blair Seidler.`): print(``): print(`Please report bugs to blair at math dot rutgers dot edu`): print(``): print(`For a list of the procedures type HelpCB();, for help with`): print(`a specific procedure, type HelpCB(procedure_name); .`): print(``): with(combinat): HelpCB:=proc(): if args=NULL then print(`The main procedures are: `): print(`FindCanonBF, FindCanonBFP, FindCanonBFNP`): print(`CompressToBEC.`): elif nops([args])=1 and op(1,[args])=FindCanonBF then print(`FindCanonBF(n): inputs an integer n and`): print(`outputs the set of Boolean functions which`): print(`are indistiguishable under complementation.`): print(``): print(`For example, FindCanonBF(2); will output`): print(``): print(`{{}, {[1, 1]}, {[-1, 1], [1, -1]}, {[-1, 1], [1, 1]}, `): print(` {[1, -1], [1, 1]}, {[-1, 1], [1, -1], [1, 1]}, `): print(` {[-1, -1], [-1, 1], [1, -1], [1, 1]}}`): elif nops([args])=1 and op(1,[args])=FindCanonBFP then print(`FindCanonBF(n): inputs an integer n and`): print(`outputs the set of Boolean functions which`): print(`are indistiguishable under permutation.`): print(``): print(`For example, FindCanonBFP(2); will output`): print(``): print(`{{}, {[-1, -1]}, {[1, -1]}, {[1, 1]}, {[-1, -1], [1, -1]},`): print(` {[-1, -1], [1, 1]}, {[-1, 1], [1, -1]}, {[1, -1], [1, 1]}, `): print(` {[-1, -1], [-1, 1], [1, -1]}, {[-1, -1], [1, -1], [1, 1]}, `): print(` {[-1, 1], [1, -1], [1, 1]}, {[-1, -1], [-1, 1], [1, -1], [1, 1]}}`): elif nops([args])=1 and op(1,[args])=FindCanonBFNP then print(`FindCanonBF(n): inputs an integer n and`): print(`outputs the set of Boolean functions which`): print(`are indistiguishable under the combination of`): print(`permutation and complementation.`): print(``): print(`For example, FindCanonBFNP(2); will output`): print(``): print(`{{}, {[1, 1]}, {[-1, -1], [1, 1]}, {[1, -1], [1, 1]},`): print(` {[-1, 1], [1, -1], [1, 1]}, {[-1, -1], [-1, 1], [1, -1], [1, 1]}}`): elif nops([args])=1 and op(1,[args])=CompressToBEC then print(`CompressToBEC(n,BF): inputs number of variables n and`): print(`a set of Boolean functions BF, and outputs`): print(`one function from each big equivalence class.`): print(`Note: a "big equivalence class" is a class of functions`): print(`are indistiguishable under the combination of`): print(`permutation and complementation.`): print(``): print(`For example, CompressToBEC(1,{{},{[-1]},{[1]}}); will output`): print(`{{}, {[-1]}}.`): else print(`There is no Help for`,args): fi: end: #IntegerToSet(k): inputs an integer k in [1,2^n] and outputs the set of #elements in {1..n} representing the positions with 1's in the binary representation # of k. IntegerToSet:=proc(k) local i,rem,L: option remember: rem:=k: L:={}: for i from 1 while rem>0 do if (rem mod 2)=1 then L:=L union {i}: fi: rem:=trunc(rem/2): od: L: end: #IntegerToVec(k,n): inputs an integer k in [0,2^n-1] (and n) and outputs the vector of #length n that corresponds to the binary representation of k (written as a list) #(with -1's padded at the front to make it of length n, if necessary) IntegerToVec:=proc(k,n) local i,rem,r,L: option remember: rem:=k: L:=[0$n]: for i from n to 1 by -1 do r:=rem mod 2: rem:=trunc(rem/2): if r=1 then L[i]:=1: else L[i]:=-1: fi: od: L: end: #VecToInteger(v): inverse of IntegerToVec(k,n) VecToInteger:=proc(v) local n,i,s: n:=nops(v): s:=0: for i from 1 to n do if v[i]=1 then s:=s+2^(n-i): fi: od: s: end: PermVec:=proc(v,p) local k: option remember: #Takes vector v and permutes positions corresponding to the permutation p [seq(v[p[k]],k=1..nops(v))]: end: ModVec:=proc(v,w) local k: option remember: #Takes vector v and negates positions corresponding to the 1's in vector w [seq(-v[k]*w[k],k=1..nops(v))]: end: NegArray:=proc(n) local i,j,NT: option remember: #Set up the array of vector negations. Note that this table #is indexed from 1..2^n,1..2^n, where 1 maps to the all -1's #vector and 2^n maps to the all 1's vector. NT:=Array(symmetric,1..2^n,1..2^n): for i from 1 to 2^n do for j from i to 2^n do NT[i,j]:=VecToInteger(ModVec(IntegerToVec(i-1,n),IntegerToVec(j-1,n)))+1: od: od: RETURN(NT): end: PermArray:=proc(n) local i,j,PT,P: option remember: #Set up the array of vector permutations. Note that this table #is indexed from 1..2^n,1..n!, where 1 maps to the all -1's #vector and 2^n maps to the all 1's vector. PT:=Array(1..2^n,1..n!): P:=permute(n): for i from 1 to 2^n do for j from 1 to nops(P) do PT[i,j]:=VecToInteger(PermVec(IntegerToVec(i-1,n),P[j]))+1: od: od: RETURN(PT): end: FindCanonBF:=proc(n) local NT,i,j,k,f,F,isnew,ff: NT:=NegArray(n): F:={}: for i from 2^(2^n) to 1 by -1 do # Generate BF's one by one f:=IntegerToSet(i-1): #print('f',f): #Compare to previously generated BF's isnew:=true: for j from 1 to 2^n do ff:={seq(NT[k,j],k in f)}: #print(f,ff): if ff in F then #print('ding'): isnew:=false: break: fi: od: if isnew then F:=F union {f}: fi: od: ff:={}: for i in F do f:={}: for j in i do f:=f union {IntegerToVec(j-1,n)}: od: ff:=ff union {f}: od: ff: end: FindCanonBFP:=proc(n) local PT,i,j,k,f,F,isnew,ff,P: PT:=PermArray(n): F:={}: for i from 2^(2^n) to 1 by -1 do # Generate BF's one by one f:=IntegerToSet(i-1): #print('f',f): #Compare to previously generated BF's isnew:=true: for j from 1 to n! do ff:={seq(PT[k,j],k in f)}: #print(f,ff): if ff in F then #print('ding'): isnew:=false: break: fi: od: if isnew then F:=F union {f}: fi: od: ff:={}: for i in F do f:={}: for j in i do f:=f union {IntegerToVec(j-1,n)}: od: ff:=ff union {f}: od: ff: end: FindCanonBFNP:=proc(n) local NT,PT,i,j,j2,k,v,bf,f,F,isnew,ff,P: NT:=NegArray(n): PT:=PermArray(n): F:={}: for i from 1 to 2^(2^n) do # Generate BF's one by one bf:=IntToBool(n,i-1): f:={seq(VecToInteger(v)+1,v in bf)}: #print('f',f): #Compare to previously generated BF's isnew:=true: for j from 1 to n! while isnew do for j2 from 1 to 2^n while isnew do ff:={seq(PT[NT[k,j2],j],k in f)}: #print(f,ff): if ff in F then #print('ding'): isnew:=false: break: fi: od: od: if isnew then F:=F union {f}: fi: od: ff:={}: for i in F do f:={}: for j in i do f:=f union {IntegerToVec(j-1,n)}: od: ff:=ff union {f}: od: ff: end: CompressToBEC:=proc(n,BF) local NT,PT,i,j,j2,k,f,F,isnew,ff,P: NT:=NegArray(n): PT:=PermArray(n): F:={}: for i in BF do # convert each function to set f:={seq(VecToInteger(j)+1,j in i)}: #print('f',f): #Compare to previously generated BF's isnew:=true: for j from 1 to n! while isnew do for j2 from 1 to 2^n while isnew do ff:={seq(PT[NT[k,j2],j],k in f)}: #print(f,ff): if ff in F then #print('ding'): isnew:=false: break: fi: od: od: if isnew then F:=F union {f}: fi: od: ff:={}: for i in F do f:={}: for j in i do f:=f union {IntegerToVec(j-1,n)}: od: ff:=ff union {f}: od: ff: end: InSameBEC:=proc(n,f1,f2) local NT,PT,ff,ff1,ff2,i,j,k: NT:=NegArray(n): PT:=PermArray(n): ff1:={seq(VecToInteger(j)+1,j in f1)}: ff2:={seq(VecToInteger(j)+1,j in f2)}: for i from 1 to n! do for j from 1 to 2^n do ff:={seq(PT[NT[k,j],i],k in ff2)}: if ff=ff1 then RETURN(true): fi: od: od: RETURN(false): end: GetBECrep:=proc(n,f1) local NT,PT,f,fn,ff,ff1,i,j,k,min: NT:=NegArray(n): PT:=PermArray(n): min:=BoolToInt(n, f1): ff1:={seq(VecToInteger(j)+1,j in f1)}: #ff2:={seq(VecToInteger(j)+1,j in f2)}: for i from 1 to n! do for j from 1 to 2^n do ff:={seq(PT[NT[k,j],i],k in ff1)}: f:={seq(IntegerToVec(k-1,n),k in ff)}: fn:=BoolToInt(n,f): if fn0 do slp:=RandSLPn(n,g): #print("checking",slp): slpf:=BoolToInt(n,SLPntoBool(slp)): #print("function",slpf): if T[slpf]=NF then #print(f): #print(slp): F:=F minus {slpf}: T[slpf]:=slp: numfd:=numfd+1: fi: if i mod 10000=0 then print(i): fi: od: #print(T): if FileTools[Exists](fnold) then FileTools[Remove](fnold): fi: FileTools[Rename](fn,fnold): FileTools[Text][WriteLine](fn, cat("Functions: ",convert(numf,string)," Found: ",convert(numfd,string))): for f in indices(T,indexorder) do #print(i,T[op(f)]): #print("NF"): FileTools[Text][WriteLine](fn, cat(convert(op(f),string),": ",convert(IntToBool(n,op(f)),string))): FileTools[Text][WriteLine](fn, convert(T[op(f)],string)): #print("ding"): FileTools[Text][WriteLine](fn, ""): od: FileTools[Text][Close](fn): #print(T): F: end: GenBFCatFileAll:=proc(n) local fn,F,f,T: fn:=cat("BFCatFileA",n,"var.txt"): F:=FindCanonBFNP(n): T := table(): for f in F do T[BoolToInt(n, f)] := f; od: FileTools[Text][WriteLine](fn, cat("Functions: ",convert(nops(F),string)," Found: 0")): for f in indices(T,indexorder) do FileTools[Text][WriteLine](fn, cat(convert(op(f),string),": ",convert(T[op(f)],string))): FileTools[Text][WriteLine](fn, "NF"): FileTools[Text][WriteLine](fn, ""): od: FileTools[Text][Close](fn): fn: end: CatFileFindSLPAll:=proc(n,g) local numf,numfd,l1,l2,l3,fn,fnold,A,T,F,f,SLP,slp,slpf,i: fn:=cat("BFCatFileA",n,"var.txt"): fnold:=cat("BFCatFileA",n,"var-OLD.txt"): FileTools[Text][Open](fn): l1:=FileTools[Text][ReadLine](fn): l2:=sscanf(l1,"%s %d %s %d"): numf:=l2[2]: numfd:=l2[4]: #print("Functions",numf,"Found",numfd): F:={}: T:=table(): for i from 1 to numf do l1:=FileTools[Text][ReadLine](fn): #print(l1): if l1=NULL then break: fi: l2:=FileTools[Text][ReadLine](fn): l3:=FileTools[Text][ReadLine](fn): l3:=sscanf(l1,"%d: %s"): f:=l3[1]: T[f]:=parse(l2): if l2="NF" then F:=F union {f}: fi: od: #print(T,F): FileTools[Text][Close](fn): #print(A): #print(T): SLP:=AllSLPn(n,g): print("Have SLP's - entering loop"): print(nops(SLP)," - number of SLPs"): for i from 1 to nops(SLP) while nops(F)>0 do slp:=SLP[i]: #print("checking",slp): slpf:=BoolToInt(n,SLPntoBool(slp)): #print("function",slpf): if T[slpf]=NF then #print(f): #print(slp): F:=F minus {slpf}: T[slpf]:=slp: numfd:=numfd+1: fi: if i mod 1000=0 then print(i): fi: od: #print(T): if FileTools[Exists](fnold) then FileTools[Remove](fnold): fi: FileTools[Rename](fn,fnold): FileTools[Text][WriteLine](fn, cat("Functions: ",convert(numf,string)," Found: ",convert(numfd,string))): for f in indices(T,indexorder) do #print(i,T[op(f)]): #print("NF"): FileTools[Text][WriteLine](fn, cat(convert(op(f),string),": ",convert(IntToBool(n,op(f)),string))): FileTools[Text][WriteLine](fn, convert(T[op(f)],string)): #print("ding"): FileTools[Text][WriteLine](fn, ""): od: FileTools[Text][Close](fn): #print(T): F: end: AnalyzeSLPAfile:=proc(n,fnstem) local numf,numfd,l1,l2,l3,l4,fn,A,T,G,F,f,SLP,slp,slpf,i,numg,maxg: fn:=cat(fnstem,".txt"): FileTools[Text][Open](fn): l1:=FileTools[Text][ReadLine](fn): l2:=sscanf(l1,"%s %d %s %d"): numf:=l2[2]: numfd:=l2[4]: #print("Functions",numf,"Found",numfd): F:={}: T:=table(): for i from 1 to numf do l1:=FileTools[Text][ReadLine](fn): #print(l1): if l1=NULL then break: fi: l2:=FileTools[Text][ReadLine](fn): l3:=FileTools[Text][ReadLine](fn): l4:=sscanf(l1,"%d: %s"): f:=l4[1]: T[f]:=parse(l2): F:=F union {f}: od: #print(T,F): FileTools[Text][Close](fn): maxg:=0: G:=table(): for f in F do: if nops(T[f][-1])<3 then numg:=0: else numg:=nops(T[f])-n: fi: if numg>maxg then maxg:=numg: fi: #print(T[f],nops(T[f])): #print(T[f][-1],nops(T[f][-1])): #print(cat("Function ",convert(f,string)," requires ",convert(numg,string)," gates.")): G[f]:=numg: od: A:=table([seq(i={},i=0..maxg)]): for f in F do A[G[f]]:=A[G[f]] union {f}: od: print(A): for i from 0 to maxg do print(cat(convert(i,string)," gates: ",convert(nops(A[i]),string)," functions - ",convert(A[i],string))): od: end: IsMonotone:=proc(n,f) local i,j,v,s,nv: if nops(f)=0 then RETURN(true): fi: for v in f do s:={}: for i from 1 to n do if v[i]=-1 then s:=s union {i}: fi: od: for i in powerset(s) do: nv:=v: for j in i do nv[j]:=1: od: if not (nv in f) then RETURN(false): fi: od: od: RETURN(true): end: FindMonoBFP:=proc(n) local PT,i,j,v,nv,nv2,f,F,ff,W: F:={{}}: #Adds empty function because it is only monotone function not containing [1$n] W:={{[1$n]}}: # Adds the AND of all variables as the seed function in working set W while nops(W)>0 do ff:=W[1]: #pull function from W #print(ff): if not (ff in F) then F:=F union {ff}: for v in ff do for i from 1 to n do if v[i]=1 then nv:=[op(1..i-1,v),-1,op(i+1..n,v)]: f:=ff union {nv}: #print(ff,f): if not (f in F) and IsMonotone(n,f) then W:=W union {f}: fi: fi: od: od: fi: W:=W minus {ff}: od: ff:={}: for i in F do if IsBECrep(n,i) then ff:=ff union {i}: fi: od: ff: end: GenMonoCatFileAll:=proc(n) local fn,F,f,T: fn:=cat("MonoCatFileA",n,"var.txt"): F:=FindMonoBFP(n): T := table(): for f in F do T[BoolToInt(n, f)] := f; od: FileTools[Text][WriteLine](fn, cat("Functions: ",convert(nops(F),string)," Found: 0")): for f in indices(T,indexorder) do FileTools[Text][WriteLine](fn, cat(convert(op(f),string),": ",convert(T[op(f)],string))): FileTools[Text][WriteLine](fn, "NF"): FileTools[Text][WriteLine](fn, ""): od: FileTools[Text][Close](fn): fn: end: MonoFileFindSLPAll:=proc(n,g) local numf,numfd,l1,l2,l3,fn,fnold,A,T,F,f,SLP,slp,slpf,i: fn:=cat("MonoCatFileA",n,"var.txt"): fnold:=cat("MonoCatFileA",n,"var-OLD.txt"): FileTools[Text][Open](fn): l1:=FileTools[Text][ReadLine](fn): l2:=sscanf(l1,"%s %d %s %d"): numf:=l2[2]: numfd:=l2[4]: #print("Functions",numf,"Found",numfd): F:={}: T:=table(): for i from 1 to numf do l1:=FileTools[Text][ReadLine](fn): #print(l1): if l1=NULL then break: fi: l2:=FileTools[Text][ReadLine](fn): l3:=FileTools[Text][ReadLine](fn): l3:=sscanf(l1,"%d: %s"): f:=l3[1]: T[f]:=parse(l2): if l2="NF" then F:=F union {f}: fi: od: #print(T,F): FileTools[Text][Close](fn): #print(A): #print(T): SLP:=AllMonoSLPn(n,g): print("Have SLP's - entering loop"): print(nops(SLP)," - number of SLPs"): for i from 1 to nops(SLP) while nops(F)>0 do slp:=SLP[i]: #print("checking",slp): slpf:=BoolToInt(n,SLPntoBool(slp)): #print("function",slpf): if T[slpf]=NF then #print(f): #print(slp): F:=F minus {slpf}: T[slpf]:=slp: numfd:=numfd+1: fi: if i mod 1000=0 then print(i): fi: od: #print(T): if FileTools[Exists](fnold) then FileTools[Remove](fnold): fi: FileTools[Rename](fn,fnold): FileTools[Text][WriteLine](fn, cat("Functions: ",convert(numf,string)," Found: ",convert(numfd,string))): for f in indices(T,indexorder) do #print(i,T[op(f)]): #print("NF"): FileTools[Text][WriteLine](fn, cat(convert(op(f),string),": ",convert(IntToBool(n,op(f)),string))): FileTools[Text][WriteLine](fn, convert(T[op(f)],string)): #print("ding"): FileTools[Text][WriteLine](fn, ""): od: FileTools[Text][Close](fn): #print(T): F: end: CatFileToBfile:=proc(fnstem,offset) local numf,numfd,l1,l2,l3,l4,fn,bfn,f,i: fn:=cat(fnstem,".txt"): bfn:=cat("b-",fnstem,".txt"): FileTools[Text][Open](fn): l1:=FileTools[Text][ReadLine](fn): l2:=sscanf(l1,"%s %d %s %d"): numf:=l2[2]: numfd:=l2[4]: #print("Functions",numf,"Found",numfd): for i from 1 to numf do l1:=FileTools[Text][ReadLine](fn): #print(l1): if l1=NULL then break: fi: l2:=FileTools[Text][ReadLine](fn): l3:=FileTools[Text][ReadLine](fn): l4:=sscanf(l1,"%d: %s"): f:=l4[1]: FileTools[Text][WriteLine](bfn,cat(convert(i+offset-1,string)," ",convert(f,string))): od: #print(T,F): FileTools[Text][Close](fn): FileTools[Text][Close](bfn): end: CatFileToSeqList:=proc(fnstem) local numf,numfd,l1,l2,l3,l4,fn,outstr,f,i: fn:=cat(fnstem,".txt"): FileTools[Text][Open](fn): l1:=FileTools[Text][ReadLine](fn): l2:=sscanf(l1,"%s %d %s %d"): numf:=l2[2]: numfd:=l2[4]: #print("Functions",numf,"Found",numfd): for i from 1 to min(80,numf) do l1:=FileTools[Text][ReadLine](fn): #print(l1): if l1=NULL then break: fi: l2:=FileTools[Text][ReadLine](fn): l3:=FileTools[Text][ReadLine](fn): l4:=sscanf(l1,"%d: %s"): f:=l4[1]: if i=1 then outstr:=convert(f,string): else outstr:=cat(outstr,",",convert(f,string)): fi: od: #print(T,F): FileTools[Text][Close](fn): outstr: end: