diff options
Diffstat (limited to 'Build/source/utils/asymptote/base/syzygy.asy')
-rw-r--r-- | Build/source/utils/asymptote/base/syzygy.asy | 936 |
1 files changed, 936 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/base/syzygy.asy b/Build/source/utils/asymptote/base/syzygy.asy new file mode 100644 index 00000000000..dce87805c9d --- /dev/null +++ b/Build/source/utils/asymptote/base/syzygy.asy @@ -0,0 +1,936 @@ +/***** syzygy.asy {{{1 + * Andy Hammerlindl 2006/12/02 + * + * Automates the drawing of braids, relations, and syzygies, along with the + * corresponding equations. + * + * See + * http://katlas.math.toronto.edu/drorbn/index.php?title=06-1350/Syzygies_in_Asymptote + * For more information. + *****/ +struct Component { // {{{1 + // The number of strings coming in or out of the component. + int in; + int out; + + // Which 'out' string each 'in' string is connected to. For deriving + // equations. + int[] connections; + + string symbol; // For pullback notation. + string lsym; // For linear equations. + string codename; // For Mathematica code. + + guide[] draw(picture pic, guide[] ins); +} + +// Utility functions {{{1 +pair[] endpoints(guide[] a) { + pair[] z; + for (int i=0; i<a.length; ++i) + z.push(endpoint(a[i])); + return z; +} + +pair min(pair[] z) { + pair m=(infinity, infinity); + for (int i=0; i<z.length; ++i) { + if (z[i].x < m.x) + m=(z[i].x,m.y); + if (z[i].y < m.y) + m=(m.x,z[i].y); + } + return m; +} + +pair max(pair[] z) { + pair M=(-infinity, -infinity); + for (int i=0; i<z.length; ++i) { + if (z[i].x > M.x) + M=(z[i].x,M.y); + if (z[i].y > M.y) + M=(M.x,z[i].y); + } + return M; +} + +// Component Definitions {{{1 +real hwratio=1.4; +real gapfactor=6; + +Component bp=new Component; +bp.in=2; bp.out=2; +bp.connections=new int[] {1,0}; +bp.symbol="B^+"; bp.lsym="b^+"; bp.codename="bp"; +bp.draw=new guide[] (picture pic, guide[] ins) { + pair[] z=endpoints(ins); + pair m=min(z), M=max(z); + real w=M.x-m.x, h=hwratio*w; + pair centre=(0.5(m.x+M.x),M.y+h/2); + + /* + return new guide[] {ins[1]..centre{NW}..z[0]+h*N, + ins[0]..centre{NE}..z[1]+h*N}; + */ + + real offset=gapfactor*linewidth(currentpen); + draw(pic, ins[1]..(centre-offset*NW){NW}); + return new guide[] {(centre+offset*NW){NW}..z[0]+h*N, + ins[0]..centre{NE}..z[1]+h*N}; +}; + +Component bm=new Component; +bm.in=2; bm.out=2; +bm.connections=new int[] {1,0}; +bm.symbol="B^-"; bm.lsym="b^-"; bm.codename="bm"; +bm.draw=new guide[] (picture pic, guide[] ins) { + pair[] z=endpoints(ins); + pair m=min(z), M=max(z); + real w=M.x-m.x, h=hwratio*w; + pair centre=(0.5(m.x+M.x),M.y+h/2); + + /* + return new guide[] {ins[1]..centre{NW}..z[0]+h*N, + ins[0]..centre{NE}..z[1]+h*N}; + */ + + real offset=gapfactor*linewidth(currentpen); + draw(pic, ins[0]..(centre-offset*NE){NE}); + return new guide[] {ins[1]..centre{NW}..z[0]+h*N, + (centre+offset*NE){NE}..z[1]+h*N}; +}; + +Component phi=new Component; +phi.in=2; phi.out=1; +phi.connections=new int[] {0,0}; +phi.symbol="\Phi"; phi.lsym="\phi"; phi.codename="phi"; +phi.draw=new guide[] (picture pic, guide[] ins) { + pair[] z=endpoints(ins); + pair m=min(z), M=max(z); + real w=M.x-m.x, h=hwratio*w; + pair centre=(0.5(m.x+M.x),M.y+h/2); + + + //real offset=4*linewidth(currentpen); + draw(pic, ins[0]..centre{NE}); + draw(pic, ins[1]..centre{NW}); + draw(pic, centre,linewidth(5*linewidth(currentpen))); + dot(pic, centre); + return new guide[] {centre..centre+0.5h*N}; +}; + +Component wye=new Component; +wye.in=1; wye.out=2; +wye.connections=null; // TODO: Fix this! +wye.symbol="Y"; wye.lsym="y"; wye.codename="wye"; +wye.draw=new guide[] (picture pic, guide[] ins) { + pair z=endpoint(ins[0]); + real w=10, h=hwratio*w; // The 10 is a guess here, and may produce badness. + pair centre=(z.x,z.y+h/2); + + + draw(pic, ins[0]..centre); + draw(pic, centre,linewidth(5*linewidth(currentpen))); + return new guide[] {centre{NW}..centre+(-0.5w,0.5h), + centre{NE}..centre+(0.5w,0.5h)}; +}; + + +struct Braid { // {{{1 + // Members {{{2 + // Number of lines initially. + int n; + + struct Placement { + Component c; + int place; + + Placement copy() { + Placement p=new Placement; + p.c=this.c; p.place=this.place; + return p; + } + } + Placement[] places; + + void add(Component c, int place) { + Placement p=new Placement; + p.c=c; p.place=place; + places.push(p); + } + + void add(Braid sub, int place) { + for (int i=0; i<sub.places.length; ++i) + add(sub.places[i].c,sub.places[i].place+place); + } + + // Drawing {{{2 + guide[] drawStep(picture pic, Placement p, guide[] ins) { + int i=0,j=0; + + // Draw the component. + Component c=p.c; + //write("drawing "+c.symbol+" at place "+(string)p.place); + guide[] couts=c.draw(pic, ins[sequence(c.in)+p.place]); + + pair M=max(endpoints(couts)); + + // Extend lines not in the component. + guide[] outs; + pair[] z=endpoints(ins); + while (i<p.place) { + outs.push(ins[i]..(z[i].x,M.y)); + ++i; + } + + outs.append(couts); + i+=c.in; + + while (i<ins.length) { + outs.push(ins[i]..(z[i].x,M.y)); + ++i; + } + + return outs; + } + + void drawEnd(picture pic, guide[] ins, real minheight=0) { + pair[] z=endpoints(ins); + for (int i=0; i<ins.length; ++i) { + draw(pic, z[i].y >= minheight ? ins[i] : ins[i]..(z[i].x,minheight)); + } + } + + void draw(picture pic, guide[] ins, real minheight=0) { + int steps=places.length; + + guide[] nodes=ins; + for (int i=0; i<steps; ++i) { + Placement p=places[i]; + nodes=drawStep(pic, places[i], nodes); + } + + drawEnd(pic, nodes, minheight); + } + + void draw(picture pic=currentpicture, real spacing=15, + real minheight=2hwratio*spacing) { + pair[] ins; + for (int i=0; i<n; ++i) + ins.push((spacing*i,0)); + + draw(pic, ins, minheight); + } + + // Utilities {{{2 + int in() { + return n; + } + int out() { + int steps=places.length; + int num=n; // The number of nodes at this step. + + for (int i=0; i<steps; ++i) { + Placement p=places[i]; + int nextNum=num-p.c.in+p.c.out; + num=nextNum; + } + return num; + } + + // Deep copy of a braid. + Braid copy() { + Braid b=new Braid; + b.n=this.n; + for (int i=0; i<this.places.length; ++i) + b.add(this.places[i].c,this.places[i].place); + return b; + } + + // Matching {{{2 + // Tests if a component p can be swapped with a component q which is assumed + // to be directly above it. + static bool swapable(Placement p, Placement q) { + return p.place + p.c.out <= q.place || // p is left of q or + q.place + q.c.in <= p.place; // q is left of p + } + + // Creates a new braid with a transposition of two components. + Braid swap(int i, int j) { + if (i>j) + return swap(j,i); + else { + assert(j==i+1); assert(swapable(places[i],places[j])); + + Placement p=places[i].copy(); + Placement q=places[j].copy(); + /*write("swap:"); + write("p originally at " + (string)p.place); + write("q originally at " + (string)q.place); + write("p.c.in: " + (string)p.c.in + " p.c.out: " + (string)p.c.out); + write("q.c.in: " + (string)q.c.in + " q.c.out: " + (string)q.c.out);*/ + if (q.place + q.c.in <= p.place) + // q is left of p - adjust for q renumbering strings. + p.place+=q.c.out-q.c.in; + else if (p.place + p.c.out <= q.place) + // q is right of p - adjust for p renumbering strings. + q.place+=p.c.in-p.c.out; + else + // q is directly on top of p + assert(false, "swapable"); + + /*write("q now at " + (string)q.place); + write("p now at " + (string)p.place);*/ + + Braid b=this.copy(); + b.places[i]=q; + b.places[j]=p; + return b; + } + } + + // Tests if the component at index 'start' can be moved to index 'end' + // without interfering with other components. + bool moveable(int start, int end) { + assert(start<places.length); assert(end<places.length); + if (start==end) + return true; + else if (end<start) + return moveable(end,start); + else { + assert(start<end); + Placement p=places[start].copy(); + for (int step=start; step<end; ++step) { + Placement q=places[step+1]; + if (q.place + q.c.in <= p.place) + // q is left of p - adjust for q renumbering strings. + p.place+=q.c.out-q.c.in; + else if (p.place + p.c.out <= q.place) + // q is right of p - nothing to do. + continue; + else + // q is directly on top of p + return false; + } + return true; + } + } + + bool matchComponent(Braid sub, int subindex, int place, int step) { + int i=subindex; + return sub.places[i].c == this.places[step].c && + sub.places[i].place + place == this.places[step].place; + } + + // Returns true if a sub-braid occurs within the one at the specified + // coordinates with no component occuring anywhere inbetween. + bool exactMatch(Braid sub, int place, int step) { + for (int i=0; i<sub.places.length; ++i) { + if (!matchComponent(sub, i, place, i+step)) { + write("match failed at iteration: ", i); + return false; + } + } + return true; + } + + /* + bool findSubsequence(Braid sub, int place, int size, int[] acc) { + // If we've matched all the components, we've won. + if (acc.length >= sub.places.length) + return true; + + // The next component to match. + Placement p=sub.places[acc.length]; + + // Start looking immediately after the last match. + for (int step=acc[acc.length-1]+1; step<this.places.length; ++step) { + Placement q=this.places[step]; + */ + + bool tryMatch(Braid sub, int place, int size, int[] acc) { + // If we've matched all the components, we've won. + if (acc.length >= sub.places.length) + return true; + + // The next component to match. + Placement p=sub.places[acc.length]; + + // Start looking immediately after the last match. + for (int step=acc[acc.length-1]+1; step<this.places.length; ++step) { + Placement q=this.places[step]; + // Check if the next component is in the set of strings used by the + // subbraid. + if (q.place + q.c.in > place && q.place < place + size) { + // It's in the window, so it must match the next component in the + // subbraid. + if (p.c==q.c && p.place+place==q.place) { + // A match - go on to the next component. + acc.push(step); + return tryMatch(sub, place, size, acc); // TODO: Adjust place/size. + } + else + return false; + } + + // TODO: Adjust place and size. + } + + // We've run out of components to match. + return false; + } + + + // This attempts to find a subbraid within the braid. It allows other + // components to be interspersed with the components of the subbraid so long + // as they don't occur on the same string as the ones the subbraid lies on. + // Returns null on failure. + int[] match(Braid sub, int place) { + for (int i=0; i<=this.places.length-sub.places.length; ++i) { + // Find where the first component of the subbraid matches and try to + // match the rest of the braid starting from there. + if (matchComponent(sub, 0, place, i)) { + int[] result; + result.push(i); + if (tryMatch(sub,place,sub.n,result)) + return result; + } + } + return null; + } + + // Equations {{{2 + // Returns the string that 'place' moves to when going through the section + // with Placement p. + static int advancePast(Placement p, int place) { + // If it's to the left of the component, it is unaffected. + return place<p.place ? place : + // If it's to the right of the component, adjust the numbering due + // to the change of the number of strings in the component. + p.place+p.c.in <= place ? place - p.c.in + p.c.out : + // If it's in the component, ask the component to do the work. + p.place + p.c.connections[place-p.place]; + } + + // Adjust the place (at step 0) to the step given, to find which string it is + // on in that part of the diagram. + int advanceToStep(int step, int place) { + assert(place>=0 && place<n); + assert(step>=0 && step<places.length); + + for (int i=0; i<step; ++i) + place=advancePast(places[i], place); + + return place; + } + + int pullbackWindowPlace(int step, int place, + int w_place, int w_size) { + place=advanceToStep(step,place); + return place < w_place ? 1 : // The shielding. + w_place + w_size <= place ? 0 : // The string doesn't touch it. + place-w_place+2; + } + + int pullbackPlace(int step, int place) { + // Move to the right step. + //write("advance: ", step, place, advanceToStep(step,place)); + //place=advanceToStep(step,place); + Placement p=places[step]; + return pullbackWindowPlace(step,place, p.place, p.c.in); + /*return place < p.place ? 1 : // The shielding. + p.place + p.c.in <= place ? 0 : // The string doesn't touch it. + place-p.place+2;*/ + } + + int[] pullbackWindow(int step, int w_place, int w_size) { + int[] a={1}; + for (int place=0; place<n; ++place) + a.push(pullbackWindowPlace(step, place, w_place, w_size)); + return a; + } + + int[] pullback(int step) { + Placement p=places[step]; + return pullbackWindow(step, p.place, p.c.in); + /*int[] a={1}; + for (int place=0; place<n; ++place) + a.push(pullbackPlace(step, place)); + return a;*/ + } + + string stepToFormula(int step) { + // Determine the pullbacks. + string s="(1"; + for (int place=0; place<n; ++place) + //write("pullback: ", step, place, pullbackString(step,place)); + s+=(string)pullbackPlace(step, place); + s+=")^\star "+places[step].c.symbol; + return s; + } + + // Write it as a formula with pullback notation. + string toFormula() { + if (places.length==0) + return "1"; + else { + string s; + for (int step=0; step<places.length; ++step) { + if (step>0) + s+=" "; + s+=stepToFormula(step); + } + return s; + } + } + + string windowToLinear(int step, int w_place, int w_size) { + int[] a=pullbackWindow(step, w_place, w_size); + string s="("; + for (int arg=1; arg<=w_size+1; ++arg) { + if (arg>1) + s+=","; + bool first=true; + for (int var=0; var<a.length; ++var) { + if (a[var]==arg) { + if (first) + first=false; + else + s+="+"; + s+="x_"+(string)(var+1); + } + } + } + return s+")"; + } + + string windowToCode(int step, int w_place, int w_size) { + int[] a=pullbackWindow(step, w_place, w_size); + string s="["; + for (int arg=1; arg<=w_size+1; ++arg) { + if (arg>1) + s+=", "; + bool first=true; + for (int var=0; var<a.length; ++var) { + if (a[var]==arg) { + if (first) + first=false; + else + s+=" + "; + s+="x"+(string)(var+1); + } + } + } + return s+"]"; + } + + string stepToLinear(int step) { + //int[] a=pullback(step); + Placement p=places[step]; + return p.c.lsym+windowToLinear(step, p.place, p.c.in); + + /*string s=p.c.lsym+"("; + for (int arg=1; arg<=p.c.in+1; ++arg) { + if (arg>1) + s+=","; + bool first=true; + for (int var=0; var<a.length; ++var) { + if (a[var]==arg) { + if (first) + first=false; + else + s+="+"; + s+="x_"+(string)(var+1); + } + } + } + return s+")";*/ + } + + string stepToCode(int step) { + Placement p=places[step]; + return p.c.codename+windowToCode(step, p.place, p.c.in); + } + + string toLinear(bool subtract=false) { + if (places.length==0) + return subtract ? "0" : ""; // or "1" ? + else { + string s = subtract ? " - " : ""; + for (int step=0; step<places.length; ++step) { + if (step>0) + s+= subtract ? " - " : " + "; + s+=stepToLinear(step); + } + return s; + } + } + + string toCode(bool subtract=false) { + if (places.length==0) + return subtract ? "0" : ""; // or "1" ? + else { + string s = subtract ? " - " : ""; + for (int step=0; step<places.length; ++step) { + if (step>0) + s+= subtract ? " - " : " + "; + s+=stepToCode(step); + } + return s; + } + } +} + +struct Relation { // {{{1 + Braid lhs, rhs; + + string lsym, codename; + bool inverted=false; + + string toFormula() { + return lhs.toFormula() + " = " + rhs.toFormula(); + } + + string linearName() { + assert(lhs.n==rhs.n); + assert(lsym!=""); + + string s=(inverted ? "-" : "") + lsym+"("; + for (int i=1; i<=lhs.n+1; ++i) { + if (i>1) + s+=","; + s+="x_"+(string)i; + } + return s+")"; + } + + string fullCodeName() { + assert(lhs.n==rhs.n); + assert(codename!=""); + + string s=(inverted ? "minus" : "") + codename+"["; + for (int i=1; i<=lhs.n+1; ++i) { + if (i>1) + s+=", "; + s+="x"+(string)i+"_"; + } + return s+"]"; + } + + string toLinear() { + return linearName() + " = " + lhs.toLinear() + rhs.toLinear(true); + } + + string toCode() { + return fullCodeName() + " :> " + lhs.toCode() + rhs.toCode(true); + } + + void draw(picture pic=currentpicture) { + picture left; lhs.draw(left); + frame l=left.fit(); + picture right; rhs.draw(right); + frame r=right.fit(); + + real xpad=30; + + add(pic, l); + label(pic, "=", (max(l).x + 0.5xpad, 0.25(max(l).y+max(r).y))); + add(pic, r, (max(l).x+xpad,0)); + } +} + +Relation operator- (Relation r) { + Relation opposite; + opposite.lhs=r.rhs; + opposite.rhs=r.lhs; + opposite.lsym=r.lsym; + opposite.codename=r.codename; + opposite.inverted=!r.inverted; + return opposite; +} + + +Braid apply(Relation r, Braid b, int step, int place) { + bool valid=b.exactMatch(r.lhs,place,step); + if (valid) { + Braid result=new Braid; + result.n=b.n; + for (int i=0; i<step; ++i) + result.places.push(b.places[i]); + result.add(r.rhs,place); + for (int i=step+r.lhs.places.length; i<b.places.length; ++i) + result.places.push(b.places[i]); + return result; + } + else { + write("Invalid match!"); + return null; + } +} + +// Tableau {{{1 +frame[] fit(picture[] pics) { + frame[] f; + for (int i=0; i<pics.length; ++i) { + frame ff=pics[i].fit(); + //label(ff, (string)i, (10,10)); + //f.push(pics[i].fit()); + f.push(ff); + } + return f; +} + +// Draw a number of frames in a nice circular arrangement. +picture tableau(frame[] cards, bool number=false) { + int n=cards.length; + + // Calculate the max height and width of the frames (assuming min(f)=(0,0)). + pair M=(0,0); + for (int i=0; i<n; ++i) { + pair z=max(cards[i]); + if (z.x > M.x) + M=(z.x,M.y); + if (z.y > M.y) + M=(M.x,z.y); + } + + picture pic; + real xpad=2.0, ypad=1.3; + void place(int index, real row, real column) { + pair z=((M.x*xpad)*column,(M.y*ypad)*row); + add(pic, cards[index], z); + if (number) { + label(pic,(string)index, z+(0.5M.x,0), S); + } + } + + // Handle small collections. + if (n<=4) { + for (int i=0; i<n; ++i) + place(i,0,i); + } + else { + int rows=quotient(n-1,2), columns=3; + + // Add the top middle card. + place(0,rows-1,1); + + // place cards down the right side. + for (int i=1; i<rows; ++i) + place(i, rows-i,2); + + // place cards at the bottom. + if (n%2==0) { + place(rows,0,2); + place(rows+1,0,1); + place(rows+2,0,0); + } + else { + place(rows,0,1.5); + place(rows+1,0,0.5); + } + + // place cards up the left side. + for (int i=1; i<rows; ++i) + place(i+n-rows,i,0); + } + + return pic; +} + +struct Syzygy { // {{{1 + // Setup {{{2 + Braid initial=null; + bool cyclic=true; + bool showall=false; + bool number=false; // Number the diagrams when drawn. + + string lsym, codename; + + bool watched=false; + bool uptodate=true; + + struct Move { + Braid action(Braid); + Relation rel; + int place, step; + } + + Move[] moves; + + void apply(Relation r, int step, int place) { + Move m=new Move; + m.rel=r; + m.place=place; m.step=step; + m.action=new Braid (Braid b) { + return apply(r, b, step, place); + }; + moves.push(m); + + uptodate = false; + } + + void swap(int i, int j) { + Move m=new Move; + m.rel=null; + m.action=new Braid (Braid b) { + return b.swap(i, j); + }; + moves.push(m); + + uptodate = false; + } + + // Drawing {{{2 + picture[] drawMoves() { + picture[] pics; + + assert(initial!=null, "must set initial braid"); + Braid b=initial; + + picture pic; + b.draw(pic); + pics.push(pic); + + for (int i=0; i<moves.length; ++i) { + b=moves[i].action(b); + if (showall || moves[i].rel != null) { + picture pic; + b.draw(pic); + pics.push(pic); + } + } + + // Remove the last picture. + if (this.cyclic) + pics.pop(); + + return pics; + } + + void draw(picture pic=currentpicture) { + pic.add(tableau(fit(drawMoves()), this.number)); + } + + void updatefunction() { + if (!uptodate) { + picture pic; this.draw(pic); + shipout(pic); + uptodate = true; + } + } + + void oldupdatefunction() = null; + + void watch() { + if (!watched) { + watched = true; + oldupdatefunction = atupdate(); + atupdate(this.updatefunction); + uptodate = false; + } + } + + void unwatch() { + assert(watched == true); + atupdate(oldupdatefunction); + uptodate = false; + } + + // Writing {{{2 + string linearName() { + assert(lsym!=""); + + string s=lsym+"("; + for (int i=1; i<=initial.n+1; ++i) { + if (i>1) + s+=","; + s+="x_"+(string)i; + } + return s+")"; + } + + string fullCodeName() { + assert(codename!=""); + + string s=codename+"["; + for (int i=1; i<=initial.n+1; ++i) { + if (i>1) + s+=", "; + s+="x"+(string)i+"_"; + } + return s+"]"; + } + + string toLinear() { + string s=linearName()+" = "; + + Braid b=initial; + bool first=true; + for (int i=0; i<moves.length; ++i) { + Move m=moves[i]; + if (m.rel != null) { + if (first) { + first=false; + if (m.rel.inverted) + s+=" - "; + } + else + s+=m.rel.inverted ? " - " : " + "; + s+=m.rel.lsym+b.windowToLinear(m.step, m.place, m.rel.lhs.n); + } + b=m.action(b); + } + + return s; + } + + string toCode() { + string s=fullCodeName()+" :> "; + + Braid b=initial; + bool first=true; + for (int i=0; i<moves.length; ++i) { + Move m=moves[i]; + if (m.rel != null) { + if (first) { + first=false; + if (m.rel.inverted) + s+=" - "; + } + else + s+=m.rel.inverted ? " - " : " + "; + s+=m.rel.codename+b.windowToCode(m.step, m.place, m.rel.lhs.n); + } + b=m.action(b); + } + + return s; + } + +} + +// Relation definitions {{{1 +// If you define more relations that you think would be useful, please email +// them to me, and I'll add them to the script. --Andy. +Relation r3; +r3.lhs.n=3; +r3.lsym="\rho_3"; r3.codename="rho3"; +r3.lhs.add(bp,0); r3.lhs.add(bp,1); r3.lhs.add(bp,0); +r3.rhs.n=3; +r3.rhs.add(bp,1); r3.rhs.add(bp,0); r3.rhs.add(bp,1); + +Relation r4a; +r4a.lhs.n=3; +r4a.lsym="\rho_{4a}"; r4a.codename="rho4a"; +r4a.lhs.add(bp,0); r4a.lhs.add(bp,1); r4a.lhs.add(phi,0); +r4a.rhs.n=3; +r4a.rhs.add(phi,1); r4a.rhs.add(bp,0); + +Relation r4b; +r4b.lhs.n=3; +r4b.lsym="\rho_{4b}"; r4b.codename="rho4b"; +r4b.lhs.add(bp,1); r4b.lhs.add(bp,0); r4b.lhs.add(phi,1); +r4b.rhs.n=3; +r4b.rhs.add(phi,0); r4b.rhs.add(bp,0); + |