##################################################################################################### ##This is ComboProject1.txt, a Maple package to generate and investigate integer sequences counting # ##the number of HAMILTONIAN CYCLES for interesting graphs that come from Chess. Generalizing # ##the famous Knight's tour in a 3 by board # ####It is the Maple package created by Team 1 in Dr. Z.'s Combinatorics Clss at # #Rutgers University, Fall 2020. # # Save this file as `ComboProject1.txt`, to use it # #Type, in a Maple session # #read `ComboProject1.txt`): # #and then to get a list of the functions type # #Help(): # #For Help with any of the functions, type # #Help(FunctionName) # #Team Leader: # #Team members: # #################################################################################################### print(``): print(`This is ComboProject1.txt, a Maple package that is part of Project 1 in Dr. Z.'s Combinatorics Class at Rutgers University, Fall 2020`): print(`Its purpose is to generate and investigate integer sequences counting `): print(`the number of HAMILTONIAN CYCLES for interesting graphs that come from Chess. Generalizing and Extending Euler's Knight's tour`): print(``): print(`VERSION OF Feb. 9, 2021`): print(`-----------------------------------------`): print(`-----------------`): print(``): print(`Team Leader: tbd `): print(``): print(`Other Team members: tbd `): print(``): print(`-----------------`): print(``): print(`------------------------------------`): print(`Added Oct. 27, 2020: William Wang detected a bug in SAW and SAWnu, that is now fixed. He won 5 dollars.`): print(`------------------------------------`): print(`For a list of all the functions type: Help(); `): print(`For Help with any of the functions, type Help(FunctionName):`): Help1:=proc(): if nargs=0 then print(`The secondary procedures are, CF, KiRtL, KiRtG, Lang, PtoW `): else Help(args): fi: end: Help:=proc() if nargs=0 then print(`The available procedures are`): print(` CGP, ContP, GP, KiG, KtG, CGP(G), KingTours3 `): print(`KingToursGF, KiTours(k,n), KtTours(k,n), KnightTours3, RoG, RookTours `): print(`SAW1(G,a,b,k,S), SAW(G), SAW1nu(G,a,b,k,S), SAWnu(G), WtEWalks `): elif nargs=1 and args[1]=CGP then print(`CGP(G): Given a graph G finds the set of Hamiltonian cycles (i.e. CLOSED paths) starting with 1 `): print(`Using a bottom up approach`): print(`Try: `): print(`CGP(KtG(3,10)[1]; `): elif nargs=1 and args[1]=CF then print(`CF(G): Given a directred graph G=[V,E] where V is the set of vertices and E is the set of edges`): print(`outputs it in canonical form, followed by the translation Try:`): print(`CF([{1,2,3},{[1,2],[1,3]}]);`): elif nargs=1 and args[1]=ContP then print(`ContP(G,P): Inputs a graph (in Canonical form) and a good path (one that has no repeats) finds the set of`): print(`good continuations.`): print(`Try: ContP(KG(3,10)[1],[1]):`): elif nargs=1 and args[1]=GP then print(`GP(G,k): The set of all good paths of length k, in the graph G, starting with 1. Try:`): print(`GP(KG(3,10)[1],30);`): elif nargs=1 and args[1]=KiG then print(`KiG(k,n): The KING graph on a k by n chess-board. It returns G,V where `): print(`G is the graph in CANONICAL FORM, where the vertices are named {1, ... ,n} (where n is the length of the list G)`): print(`and G[i] is the set of neighbors of vertex i, and V is the "dictionary" telling you the real name of vertex i.`): print(`For example, to get the graph of King's Tour on the 3 by 10 chessboard, type`): print(`KiG(3,10); `): elif nargs=1 and args[1]=KiRtG then print(`KiRtG(k,Max): The grammar of King's Tours in a chess-board of width k done empirically up to length Max`): print(`as soon as the langues for two consecutive length agree it stops. It this does not happen before Max it returns FAIL. Try:`): print(`KiRtG(2,7);`): elif nargs=1 and args[1]=KiRtL then print(`KiRtL(m,n): The language of the King's Tours in an m by n Chess board`): print(` Try: `): print(`KiRtL(3,4); `): elif nargs=1 and args[1]=KingTours3 then print(`KingTours3(t): The precomputed generating function for the number of King's tours on a 3 by n board,`): print(`in other words, the rational functions whose coefficient of t^n (in its Taylor expansion around 0) is`): print(`that number. Try:`): print(`KingTours3(t);`): elif nargs=1 and args[1]=KingToursGF then print(`KingToursGF(k,Max,t): The generating function whose coefficients (in its Taylor series) of t^n is the`): print(`number of King's tours on a k by n Chess board. It uses an empirical-linguist approach`): print(`by trying to find a grammar up to boards of length Max. If it fails, it returns FAIL.`): print(`(you can try and make Max bigger, and get a bigger computer). try:`): print(`KingToursGF(2,7,t);`): elif nargs=1 and args[1]=KiTours then print(`KiTours(k,n): set of CLOSED KING-TOURS in a k by n chessboard. `): print(`For example, type`): print(`KTours(3,4); `): elif nargs=1 and args[1]=KnightTours3 then print(`KnightTours3(z): The generating function, in z, due to Knuth (and possibly Elkins also) for the number of Knight's tours`): print(`on a 3 by n board. Try:`): print(`KnightTours3(z);`): elif nargs=1 and args[1]=KtG then print(`KtG(k,n): The Knight graph on a k by n chess-board. It returns G,V where `): print(`G is the graph in CANONICAL FORM, where the vertices are named {1, ... ,n} (where n is the length of the list G)`): print(`and G[i] is the set of neighbors of vertex i, and V is the "dictionary" telling you the real name of vertex i.`): print(`For example, to get the graph of Knight's Tour on the 3 by 10 chessboard, type`): print(`KtG(3,10); `): elif nargs=1 and args[1]=KtTours then print(`KtTours(k,n): set of CLOSED KNIGHT-TOURS in a k by n chessboard. `): print(`For example, to prove Euler wrong (who believed that it is impossible), type`): print(`KtTours(3,10); `): elif nargs=1 and args[1]=Lang then print(`Lang(Corp): Given a set of words finds the linguistic graph in canonical form followed by the`): print(`list L such that L[i] is the actual letter whose id is i. Try: `): print(`Lang(KiRtL(3,3);`): elif nargs=1 and args[1]=PtoW then print(`PtoW(P): Given a path of lattice points outputs a word in certain alphabet of triples of pairs. Try: `): print(` P:=KiTours(3,4)[1]; PtoW(P,3,4); `): elif nargs=1 and args[1]=RoG then print(`KiG(k,n): The ROOK graph on a k by n chess-board. It returns G,V where `): print(`G is the graph in CANONICAL FORM, where the vertices are named {1, ... ,n} (where n is the length of the list G)`): print(`and G[i] is the set of neighbors of vertex i, and V is the "dictionary" telling you the real name of vertex i.`): print(`For example, to get the graph of King's Tour on the 3 by 10 chessboard, type`): print(`RoG(3,10); `): elif nargs=1 and args[1]=RoTours then print(`RoTours(k,n): set of CLOSED ROOK-TOURS in a k by n chessboard. `): print(`For example, type`): print(`RoTours(3,4); `): elif nargs=1 and args[1]=SAW then print(` SAW(G): The set of Closed Hamiltonian paths in the graph G (in canonical form) starting at 1 and G[1][1] `): print(`Using a TOP down approach`): print(`same thing at CGP(G) except for orientation `): print(`Try: SAW(KtG(3,10)[1]);` ): elif nargs=1 and args[1]=SAWnu then print(` SAWnu(G): The NUMBER of Closed Hamiltonian paths in the graph G (in canonical form) starting at 1 and G[1][1] `): print(`Using a TOP down approach`): print(`same thing at CGP(G) except for orientation. Try: `): print(`SAW(KtG(3,10)[1]);`): elif nargs=1 and args[1]=WtEWalks then print(`WtEwalks(G,a,b,t): The weight enumerator, according to the weight t^LengthOfWalk, of the set of paths in a directed graph G`): print(`(given in canonical form) from vertex a to vertex b. Try:`): print(`WtEwalks([{2,3},{1,3},{1,2}],1,2,t);`): else print(`There is no Help for`): print(args): fi: end: #KtG(k,n): The Knight graph on a k by n board KtG:=proc(k,n) local V,E,i,j,T1,v,Neighs,Moves,m,pt: #The set the list of vertices in the k by n board (using matrix indexing) V:=[seq(seq(seq([i,j],j=1..n),i=1..k))]: #T1 is a labelling of the vertices in V for i from 1 to nops(V) do T1[V[i]]:=i: od: #E is a list such that its i-th entry is the set of neighbors of V[i] using the above-indexing E:=[]: for i from 1 to nops(V) do pt:=V[i]: Moves:={[1,2],[-1,2],[1,-2],[-1,-2],[2,1],[2,-1],[-2,1],[-2,-1]}: #There are up to 8 neighbors of any vertex Neighs:={seq(pt+m,m in Moves)}: #But many of them fall off the board, Neighs:=Neighs intersect convert(V,set): #We append to the "list of neighbors" the set of neighbors of the current vertex BUT in terms of their "ids" (given by T1) #getting a description of the graph in "cannical form" E:=[op(E),{seq(T1[v],v in Neighs)}]: od: #We return the graph in "canonical form" where the vertices (members of V), are labelled by positive inetegers from 1 to nops(V) #together with the "dictionary" E,V: end: #KiG(k,n): The King graph on a k by n board KiG:=proc(k,n) local V,E,i,j,T1,v,Neighs,Moves,m,pt: #The set the list of vertices in the k by n board (using matrix indexing) V:=[seq(seq(seq([i,j],j=1..n),i=1..k))]: #T1 is a labelling of the vertices in V for i from 1 to nops(V) do T1[V[i]]:=i: od: #E is a list such that its i-th entry is the set of neighbors of V[i] using the above-indexing E:=[]: for i from 1 to nops(V) do pt:=V[i]: Moves:={[1,0],[-1,0],[0,1],[0,-1],[1,1],[-1,-1],[1,-1],[-1,1]}: #There are up to 8 neighbors of any vertex Neighs:={seq(pt+m,m in Moves)}: #But many of them fall off the board, Neighs:=Neighs intersect convert(V,set): #We append to the "list of neighbors" the set of neighbors of the current vertex BUT in terms of their "ids" (given by T1) #getting a description of the graph in "cannical form" E:=[op(E),{seq(T1[v],v in Neighs)}]: od: #We return the graph in "canonical form" where the vertices (members of V), are labelled by positive inetegers from 1 to nops(V) #together with the "dictionary" E,V: end: #ContP(G,P): Inputs a graph (in Canonical form) and a good path (one that has no repeats) finds the set of #good continations. #Try: ContP(KG(3,10)[1],[1]): ContP:=proc(G,P) local S,s,v: #v is the last vertex in the path P v:=P[nops(P)]: #S is the set of legal followers S:=G[v] minus convert(P,set): #The output is the set of legal continuations obtained by appending to P all the legal continuations {seq([op(P),s], s in S)}: end: #GP(G,k): The set of all good paths of length k, in the graph G, starting with 1. Try: #GP(KG(3,10)[1],30); GP:=proc(G,k) local SetP,P: option remember: #This is the initial condition if k=1 then RETURN({[1]}): fi: #We can the procedure recursively SetP:=GP(G,k-1): #We call ContP(G,P) for each of the members of that are good so far, and take their union {seq(op(ContP(G,P)), P in SetP)}: end: #CGP(G): Given a graph G finds the set of Hamiltonian cycles starting with 1 CGP:=proc(G) local S,AllP,NeiOne,P: #AllP is the set of ALL Hamiltionain Paths AllP:=GP(G,nops(G)): NeiOne:=G[1]: #We now only include those that end with a neighbor of 1 S:={}: for P in AllP do if member(P[nops(P)],NeiOne) then S:=S union {P}: fi: od: S: end: #SAW1(G,a,b,k,S): Inputs a graph G of {1, ..., n} and two vertices a and b and a pos. integer k and a set of vertices S, outputs the #set of walks of length k from a to b that avoid S. Try #SAW1(KtG(3,5)[1],1,2,5,{}): SAW1:=proc(G,a,b,k,S) local gu,n,a1,lu,lu1,mu: option remember: n:=nops(G): if not (1<=a and a<=n and 1<=b and b<=n) then RETURN(FAIL): fi: if k=1 then if member(b,G[a]) and not member(a,S) and not member(b,S) then RETURN({[a,b]}): else RETURN({}): fi: fi: if (member(a,S) or member(b,S)) then RETURN({}): fi: mu:=G[a] minus (S union {a}): gu:={}: for a1 in mu do lu:=SAW1(G,a1,b,k-1,S union {a}): gu:=gu union {seq([a,op(lu1)],lu1 in lu)}: od: gu: end: #SAW(G): The set of Closed Hamiltonian paths in the graph G (in canonical form) starting at 1 and G[1][1] #Try: SAW(KtG(3,10)[1]): # SAW:=proc(G) local i: {seq(op(SAW1(G,1,G[1][i],nops(G)-1,{})),i=1..nops(G[1]))}: end: #SAW1nu(G,a,b,k,S): Inputs a graph G of {1, ..., n} and two vertices a and b and a pos. integer k and a set of vertices S, outputs the #NUMBER of walks of length k from a to b that avoid S. Try #SAW1nu(KtG(3,5)[1],1,2,5,{}): SAW1nu:=proc(G,a,b,k,S) local mu,n,a1: option remember: n:=nops(G): if not (1<=a and a<=n and 1<=b and b<=n) then RETURN(FAIL): fi: if k=1 then if member(b,G[a]) and not member(a,S) and not member(b,S) then RETURN(1): else RETURN(0): fi: fi: if (member(a,S) or member(b,S)) then RETURN(0): fi: mu:=G[a] minus (S union {a}): add(SAW1nu(G,a1,b,k-1,S union {a}), a1 in mu): end: #SAWnu(G): The NUMBER of Closed Hamiltonian paths in the graph G (in canonical form) starting at 1 and G[1][1] #Try: SAWnu(KtG(3,10)[1]): SAWnu:=proc(G) local i: add(SAW1nu(G,1,G[1][i],nops(G)-1,{}),i=1..nops(G[1])): end: #KtTours(k,n): The set of closed Knight-Tours in a k by n chess-board. Try: #KtTours(3,10); KtTours:=proc(k,n) local G,V,TOURS,t,i: G:=KtG(k,n): V:=G[2]: G:=G[1]: TOURS:=SAW(G): {seq([seq(V[t[i]],i=1..nops(t)),V[t[1]]],t in TOURS)}: end: #KiTours(k,n): The set of closed King-Tours in a k by n chess-board. Try: #KiTours(3,4); KiTours:=proc(k,n) local G,V,TOURS,t,i: G:=KiG(k,n): V:=G[2]: G:=G[1]: TOURS:=SAW(G): {seq([seq(V[t[i]],i=1..nops(t)),V[t[1]]],t in TOURS)}: end: #PtoW(P,m,n): Given a path of lattice points in {1,2,..,m}x {1,...,n} outputs a word in certain alphabet of m-tuples of pairs #Try: #PtoW(KiTours(3,4)[1],3,4); PtoW:=proc(P,m,n) local T,i,j: if nops(P)<>m*n+1 then print(`Bad input`): RETURN(FAIL): fi: if nops(convert(P,set))<>m*n then print(`Bad input`): RETURN(FAIL): fi: if P[1]<>[1,1] or P[-1]<>[1,1] then print(`P must start and end with`, [1,1]): RETURN(FAIL): fi: T[[1,1]]:=[P[-2]-P[-1] , P[2]-P[1]]: for i from 2 to nops(P)-1 do T[P[i]]:=[P[i-1]-P[i],P[i+1]-P[i]]: od: [seq([seq(T[[i,j]],i=1..m)],j=1..n)]: end: #KiRtL(m,n): The language of the King's Tours in an m by n Chess board #Try: #KiRtL(3,4); KiRtL:=proc(m,n) local S,P: S:=KiTours(m,n): {seq( [0,op(PtoW(P,m,n)),1], P in S)}: end: #Lang(Corp): Given a set of words finds the linguistic graph in canonical form followed by the #list L such that L[i] is the actual letter whose id is i. Try: #Lang(KiRtL(3,3)); Lang:=proc(COR) local VERTICES,EDGES,w,i1: #VERTICES IS THE SET OF ALL LETTERS THAT SHOWED UP VERTICES:={seq(op(w), w in COR)}: EDGES:={seq(seq([w[i1],w[i1+1]],i1=1..nops(w)-1), w in COR)}: [VERTICES, EDGES]: end: #CF(G): Given a directred graph G=[V,E] where V is the set of vertices and E is the set of edges #outputs it in canonical form, followed by the translation Try: #CF([{1,2,3},{[1,2],[1,3]}]); CF:=proc(G) local T,V,e,i,G1: V:=convert(G[1],list): for i from 1 to nops(V) do T[V[i]]:=i: od: for i from 1 to nops(V) do G1[i]:={}: od: for e in G[2] do G1[T[e[1]] ]:=G1[T[e[1]] ] union {T[e[2]]}: od: [seq(G1[i],i=1..nops(V))],V: end: #WtEwalks(G,a,b,t): The weight enumerator, according to the weight t^LengthOfWalk, of the set of paths in a directed graph G #(given in canonical form) from vertex a to vertex b. Try: #WtEwalks([{2,3},{1,3},{1,2}],1,2,t); WtEwalks:=proc(G,a,b,t) local eq,var,X,eq1,i,j: if not (1<=a and a<=nops(G) and 1<=b and b<=nops(G)) then print(a,b, `must be integers between 1 and `, nops(G)): RETURN(FAIL): fi: var:={seq(X[i],i=1..nops(G))}: #X[i] is the weight-enumerator of walks from i to b eq:={}: for i from 1 to nops(G) do #Every walk from i to b unless it is of length 0 (that its weight is 1) starts with a walk from a neighbor of i eq1:=X[i]-t*add(X[j], j in G[i]): #if i=b we must also include the empty walk if i=b then eq1:=eq1-1: fi: eq:=eq union {eq1}: od: normal(subs(solve(eq,var),X[a])): end: #KiRtG(k,Max): The grammar of King's Tours in a chess-board of width k done empirically up to length Max #as soon as the langues for two consecutive length agree it stops. It this does not happen before Max it returns FAIL. Try: #KiRtG(2,7); KiRtG:=proc(k,Max) local i: for i from 1 to Max do if Lang(KiRtL(k,i))=Lang(KiRtL(k,i+1)) then RETURN( CF(Lang(KiRtL(k,i)))): fi: od: FAIL: end: #KingToursGF(k,Max,t): The generating function whose coefficients (in its Taylor series) of t^n is the #number of King's tours on a k by n Chess board. It uses an empirical-linguist approach #by trying to find a grammar up to boards of length Max. If it fails, it returns FAIL. #(you can try and make Max bigger, and get a bigger computer). try: #KingToursGF(2,7,t); KingToursGF:=proc(k,Max,t) local G,f: G:=KiRtG(k,Max) : if G=FAIL then RETURN(FAIL): fi: if not (G[2][1]=0 and G[2][2]=1) then RETURN(FAIL): fi: #we divide by t since in our model where we have a starting letter 0 and an ending letter 1, #each path has one extra length f:=normal(WtEwalks(G[1],1,2,t)/t): #We manually add the coefficient of t^2 f:=normal(f+nops(KiTours(k,2))*t^2 ): #Following the convention (from Kinght's Tours) that we don't count different orienations, we divide by 2 f:=f/2: normal(f): end: #KingTours3(t): The precomputed generating function for the number of King's tours on a 3 by n board, #in other words, the rational functions whose coefficient of t^n (in its Taylor expansion around 0) is #that number. Try: #KingTours3(t); KingTours3:=proc(t): 2*t^2*(3*t^4+4*t^3+2*t^2-2)/(6*t^4+8*t^3+15*t^2+4*t-1): end: #KnightTours3(z): The generating function, in z, due to Knuth (and possibly Elkins also) for the number of Knight's tours #on a 3 by n board. Try: #KnightTours3(z); KnightTours3:=proc(z) 16 * (z^5 + 5*z^6 - 34*z^7 - 116*z^8 + 505*z^9 + 616*z^10 - 3179*z^11 - 4*z^12 + 9536*z^13 - 8176*z^14 - 13392*z^15 + 15360*z^16 + 13888*z^17 + 2784*z^18 - 3328*z^19 - 22016*z^20 + 5120*z^21 + 2048*z^22) / (1 - 6*z - 64*z^2 + 200*z^3 + 1000*z^4 - 3016*z^5 - 3488*z^6 + 24256*z^7 - 23776*z^8 - 104168*z^9 + 203408*z^10 + 184704*z^11 - 443392*z^12 - 14336*z^13 + 151296*z^14 - 145920*z^15 + 263424*z^16 - 317440*z^17 - 36864*z^18 + 966656*z^19 - 573440*z^20 - 131072*z^21): end: ######start new #RoG(k,n): The Rook graph on a k by n board RoG:=proc(k,n) local V,E,i,j,T1,v,Neighs,pt,i1,j1: #The set the list of vertices in the k by n board (using matrix indexing) V:=[seq(seq(seq([i,j],j=1..n),i=1..k))]: #T1 is a labelling of the vertices in V for i from 1 to nops(V) do T1[V[i]]:=i: od: #E is a list such that its i-th entry is the set of neighbors of V[i] using the above-indexing E:=[]: for i from 1 to nops(V) do pt:=V[i]: Neighs:={seq([pt[1],j1],j1=1..n), seq([i1,pt[2]] ,i1=1..k)} minus {pt}: #We append to the "list of neighbors" the set of neighbors of the current vertex BUT in terms of their "ids" (given by T1) #getting a description of the graph in "cannical form" E:=[op(E),{seq(T1[v],v in Neighs)}]: od: #We return the graph in "canonical form" where the vertices (members of V), are labelled by positive inetegers from 1 to nops(V) #together with the "dictionary" E,V: end: #RoTours(k,n): The set of closed King-Tours in a k by n chess-board. Try: #RoTours(3,4); RoTours:=proc(k,n) local G,V,TOURS,t,i: G:=RoG(k,n): V:=G[2]: G:=G[1]: TOURS:=SAW(G): {seq([seq(V[t[i]],i=1..nops(t)),V[t[1]]],t in TOURS)}: end: