Max Horn
  • NEW!
    NEW!  Connect Ideas Across Notes
    Save time and share insights. With Paragraph Citation, you can quote others’ work with source info built in. If someone cites your note, you’ll see a card showing where it’s used—bringing notes closer together.
    Got it
      • Create new note
      • Create a note from template
        • Sharing URL Link copied
        • /edit
        • View mode
          • Edit mode
          • View mode
          • Book mode
          • Slide mode
          Edit mode View mode Book mode Slide mode
        • Customize slides
        • Note Permission
        • Read
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Write
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invite by email
        Invitee

        This note has no invitees

      • Publish Note

        Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

        Your note will be visible on your profile and discoverable by anyone.
        Your note is now live.
        This note is visible on your profile and discoverable online.
        Everyone on the web can find and read all notes of this public team.

        Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Explore these features while you wait
        Complete general settings
        Bookmark and like published notes
        Write a few more notes
        Complete general settings
        Write a few more notes
        See published notes
        Unpublish note
        Please check the box to agree to the Community Guidelines.
        View profile
      • Commenting
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
        • Everyone
      • Suggest edit
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
      • Emoji Reply
      • Enable
      • Versions and GitHub Sync
      • Note settings
      • Note Insights New
      • Engagement control
      • Make a copy
      • Transfer ownership
      • Delete this note
      • Save as template
      • Insert from template
      • Import from
        • Dropbox
        • Google Drive
        • Gist
        • Clipboard
      • Export to
        • Dropbox
        • Google Drive
        • Gist
      • Download
        • Markdown
        • HTML
        • Raw HTML
    Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
    Create Create new note Create a note from template
    Menu
    Options
    Engagement control Make a copy Transfer ownership Delete this note
    Import from
    Dropbox Google Drive Gist Clipboard
    Export to
    Dropbox Google Drive Gist
    Download
    Markdown HTML Raw HTML
    Back
    Sharing URL Link copied
    /edit
    View mode
    • Edit mode
    • View mode
    • Book mode
    • Slide mode
    Edit mode View mode Book mode Slide mode
    Customize slides
    Note Permission
    Read
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Write
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- breaks: false --- <!--- `breaks: false` turns off hackmd rendering every line break in the source as a line break in the output --> ###### tags: `GAP` # MatrixObj and VectorObj in GAP Some thoughts for a potential Coding sprint / GAP Days focused on advancing MatrixObj/VectorObj ## What is MatrixObj/VectorObj about? Here are some links with background info - https://www.gapdays.de/gapdays2017-spring/10_topic/ - <https://www.gapdays.de/gapdays2017-fall/slides/slides-Horn-MatrixObj.html> - [Some slides by Thomas Breuer from 2017](https://www.gapdays.de/gapdays2017-fall/slides/slides-Breuer-matrixobj.pdf) TODO: upload the ref manual for GAP master branch somewhere and add a link to the MatrixObj chapter in there (and/or if GAP 4.12 is released in time, link to that) ## Lists with TODOs. - Main issue https://github.com/gap-system/gap/issues/945 - Row and column operations https://github.com/gap-system/gap/issues/3962 - Project https://github.com/gap-system/gap/projects/1 # TODO list This list is a collection of all previous TODO lists. However, it may not be up to date. ### Next steps - help finish [PR #2973](https://github.com/gap-system/gap/pull/2973) -- this updates `IsPlistMatrixRep` to the latest version of MatrixObj - create "real" MatrixObj instances wich are not lists (e.g. of type `IsPlistMatrixRep`, see above) and create tests which pass them to all kinds of GAP library functions (and packages!) and see what breaks; then try to fix that (possibly after discussing with others *how* to fix it) - implement missing functionality that is needed for the "fixes" in the previous step (e.g. clearly we need row/column operations for MatrixObj, see issue #3962) - Thomas introduced `IsMatrixOrMatrixObj`. Now we can let methods which shouldn't support list-of-list-matrices use `IsMatrixObj` and remove their compatibility code - read the existing documentation and point out what is missing / help improving it - write tutorials/howtos on ... - how to use MatrixObj (and why?) - how to use implement new MatrixObj types (and perhaps do that, to test it) - how to use convert existing code in the library and packages to work with MatrixObj (ideally while still working with old-style list-of-list matrices) ### Design Decisions - [ ] Write (or complete) a high level description of the MatrixObj interface; in particular, clearly state what a minimal implementation of IsVectorObj resp. IsMatrixObj needs to do (what methods to provide...); also explain things like "vectors are neither row nor column; product is always scalar product"; etc. - [ ] write tests which check whether a given concrete MatrixObj type implements the API correctly, see section tests. - [ ] consider writing the MatrixObj API so that it also works sanely for non-commutative base domains. E.g. `MultRowVector` should specify from which side it multiplies with scalars, and perhaps even come in both variants? - Mohamed's wish list: - The matrix should always know its ring: - allow the zero ring - do not require the ring to know all its properties, e.g., that knows later that it is the zero ring - allow noncommutative (semi)rings. - NrRows, NrColumns: - allow empty matrices - should be attributes not required during creation of the matrix (allows implementation of lazy matrices) - [ ] consider adding an API for MatrixObj which indicates if a given matrix obj is stored in row-major or column-major form: Reason: some algorithm are only optimal for one or the other. By detecting this, one could rewrite the matrix first... or invoke different implementations of the algorithm suitable for each rep.... Of course we can add this API later on, it doesn't have to be there initially. But note that some reps may be neither and/or both (think of sparse reps; or block matrices with submatrices of different types -- though perhaps the latter should simply not be done ;-) - Decide the relation between `IsMatrix` and `IsMatrixObj`. (September 08th) We are going to use the following setup: - Introduce `IsMatrixOrMatrixObj` [#4503](https://github.com/gap-system/gap/issues/4503) - `IsMatrixObj` does not imply `IsOrdinaryMatrix`. - All current implementations of `IsMatrixObj` representations are in fact intended for matrices with the ``usual`` matrix product and hence get the filter `IsOrdinaryMatrix` (explicitly) - Whenever a method requires that some of its arguments are matrices that have to be multiplied via the ``usual`` matrix product, the filter `IsOrdinaryMatrix` must be required for these arguments. (Thomas is going to make the necessary changes.) - Decide and document what `ShallowCopy` should do for IsVectorObj and IsMatrixObj objects. Idea: For IsVectorObj define and document similar behaviour as for plain lists. For IsMatrixObj document it explicitly as undefined (but some implementations may provide a method); suggest to use `MutableCopyMat` for IsMatrixObj objects. - When multiplying PlistMatrixObjs we check whether the BaseDomains are identical. Should we adopt this everywhere? matobj2.gd states that +,-,*,<,=, AddRowVector, AddMultVector are possible for vectors over the same BaseDomain and equal lengths. Should this mean `only if`? (For example objects in `Is8BitVectorRep` admit arithmetical operations when the lengths are different. I would say that the general documentation should define the behaviour only for same lengths and equal BaseDomain, and say that special vector representations may support more general cases.) If the identity of the `BaseDomain`s of two matrices is a necessary condition for the applicability of (generic) methods, for example for `KroneckerProduct`, then the `BaseDomain` for plists of plists must be as large as possible, in order to make these methods. For plists of plists of cyclotomics, `Cyclotomics` would be suitable. For plists of plists of FFE elements, we do not have analogous objects (one for each positive characteristic), but we could create them, and for matrices of polynomials etc., things are getting complicated. Alternatively, we could take the *family of the matrix entries* as the `BaseDomain`: `One`, `Zero`, `Characteristic` work in principle, `IsFinite`, `Size`, `\in` could be provided. (With this choice, computing the `BaseDomain` would be cheap, as required.) We state explicitly that we do not specify the behaviour if the base domains or dimensions or representation types do not fit. This is because of efficiency reasons. On the other hand, we could suggest to signal an error (with a useful error message if possible) except in the following cases: - Properties such as `IsOne` can return `false` if a problem occurs (such as a non-square matrix). - `Inverse` for a not invertible matrix should return `fail`, according to the general documentation of `Inverse`. (Note that `fail` results make sense only if one can rely on them --for *all* matrix object representations, and this is something which we do not want to guarentee.) - We have to define what shall happen if the result of an operation is a vector or matrix object but cannot get the same base domain the input(s). I think this happens only for - the division of a vector/matrix by a scalar, where some entry of the result is not in the base domain, and - the inversion of a matrix that is not invertible over the base domain. (Are there other dangerous operations?) In such situations, we can either signal an error or create a vector/matrix object over a different base domain, for example by delegating to `Vector( <list> )` or `Matrix( <list> )`, respectively. Once we decide what we want, this must be documented, and the available methods have to be adjusted. - Attributes for `IsMatrixObj` objects: *Storing* rank, determinant, etc. makes sense only for immutable objects; it would be dangerous/wrong to store such information in mutable matrices. We can still declare `RankMat`, `DeterminantMat`, etc. as attributes, since the feature to *store* attributes is deactivated unless one sets the filter `IsAttributeStoringRep` or installs special setter methods. Thus we could document that we recommend to implement new kinds of matrix objects in one of the following ways: 1. As always *immutable* objects, with the possibility to set the filter `IsAttributeStoringRep` or to install special attribute setter methods, 2. as copyable objects (that is, with the possibility to create mutable matrices); here the filter `IsAttributeStoringRep` should not be set, and special attribute setter methods should better not be installed. Note that `IsMatrixObj` does currently imply `IsCopyable`. We could drop `IsCopyable` from the general `IsMatrixObj` definition, and set it whenever mutable versions shall be supported. (For example, if the objects returned by `BlockMatrix` would not be lists then one could think about removing `IsCopyable` from their type. Currently `IsCopyable` is set, and `ShallowCopy` is defined to return a deep copy represented via plists.) Do we really want to force `IsCopyable` for all vector and matrix objects? One could think of an implementation where the objects are pointers to data files which are just readable. In such a situation, it is confusing that the object claims to be copyable but `ShallowCopy` or `MutableCopyMatrix` will not work. - Should more list like operations be added for vector objects? For example `Position` and `PositionProperty` would be candidates; a comment in an earlier version of the interface states that they are left out to simplify the interface. ### New MatrixObj functions, methods, operations, documentation, and tests - add some proper and complete MatrixObj implementations - finish `IsPlistMatrixRep`, see "Easy ways to help" - add a "strict" variant of `IsPlistMatrixRep` which shares 98% of the code but *removes* some features which are just there to easy compatibility with existing code but which cost in terms of performance - e.g. support for `mat[i]` returning a row of the matrix (which requires use of "proxy objects") - such a type is very useful for finding parts of functions that make exactly such assumptions about a given matrix - [a relevant issue](https://github.com/gap-system/gap/issues/2148) - add types of sparse matrices - Replace `EmptyMatrix` and `NullMapMatrix` by or make them into `IsMatrixObj` objects. In the library, change GF2 and 8bit matrix code to support empty matrices - [ ] consider allowing for `0 x n` and `m x 0` matrix objects. These can then properly remove existing hacks like `EmptyMatrix`, `EmptyRowVector`, `NullMapMatrix`, ... - [ ] also think about vectors... - [ ] we want both row and column vectors - [ ] for non-commutative base domains, row vectors should be left module elements, while column vectors should be right module elements - [ ] we probably should have a `IsVectorObj` and then `IsRowVectorObj` and `IsColumnVectorObj` specialize it (indeed, virtually all code for them could be identical, it' just a bit that decides whether we interpret something as a `1 x n` or `n x 1` - [ ] on the other hand, some people may prefer to lazily allow vectors to be both column and row vectors at the same time. This costs us some type safety, but maybe be more convenient??? at the very least, it should be easy and cheap (=fast) to convert between the two - generic methods mentioned in matobj{1,2}.gd - Provide further kinds of vectors/matrices. #### Done - introduce `OneOfBaseDomain`, `ZeroOfBaseDomain` (done) ### Adapting the library - [ ] go through all (well, at least as many as possible) functions in GAP that support matrices as input, and teach them to support `MatrixObj` - [ ] as this is a herculean effort, also think about ways to allow backwards compatibility - enhance GAP's compressed matrices to a full implementation of MatrixObj - e.g. add full support for 0xN and Nx0 matrices - run these matrices through the generic test suites being generated below, to find and fix any violation - [empty compressed vectors](https://github.com/gap-system/gap/issues/2121) - [ ] make it possible to create groups generated by matrix objs, and make sure as many things as possible "work" with it; this probably involves getting the `NiceMonomorphism` code to deal with MatrixObjs (defined over finite fields) - [ ] look for any places in the library using `IsMatrix` (as a filter), and see if we can / should adapt them for IsMatrixObj - [ ] for certain operations, we probably can use (almost?) the same code for `IsMatrix` and `IsMatrixObj`; possibly provide a way to make it easy to install a method for both at once -> but first see if there really are examples where this would be useful, collect them, then think about how to best tackle this [ ] Deal with `ConvertToVectorRep` / `ConvertToMatrixRep` - [ ] ideally, we want to get rid of them completely - [ ] of course lots of package use them, so we also need to work with package authors, and provide could migration strategies (possibly documented somewhere as well) - Change method installations that match several declarations since `IsMatrix` implies `IsMatrixObj`. (For example, `Length` is declared for `IsList`, `IsVectorObj`, and `IsMatrixObj`. So `Length` methods for `IsMatrix` match two declarations.) (Thomas is going to do this.) - In the (about 240) library methods that *require* `IsMatrix`, adjust the code according to the `IsMatrixObj` interface. - Get rid of `Zero(m[1][1])`, `m[i][j]`, and working with rows. Change `m[i][j]` to `m[i,j]`. (Only where really matrices are affected?) - In the (about 90) library methods that *call* `IsMatrix`, decide if one can use `IsMatrixObj` instead. - Check the library methods that *create* matrices: What can be done in order to choose a suitable kind of matrix? - [ ] Make it possible to create groups of MatrixObj matrices with `Group([mat1,mat2,...])`. - [ ] Teach the `NiceMonomorphism` code for groups to support `MatrixObj` - various operations on gf2 and 8bit matrix objects can silently convert them to plists-of-plists; e.g. it is allows to unbind an element in the middle, or assign something in cross characteristic, etc. etc. and all of these work and silently convert the matrix/vector. I can`t think of situations where I`d want that -- I`d really prefer to require the user to perform a manual conversion, and instead let accesses like the above generate an error, to help track down bugs. - I'd like to rename `vecmat.{gi,gd}` to e.g. `vecmat_gf2.{gi,gd}`, to indicate the purpose of those files; and then move any generic methods for (compressed) matrices and vectors in it into another file - Just remove some downrankings of methods? (There are comments in the code that this should be possible from GAP 4.10 on.) - renaming issues: - There are functions `RankMat`, `IsDiagonalMat`, etc. We should consistently introduce the corresponding names `RankMatrix`, `IsDiagonalMatrix`, etc. as main names, and keep the `Mat` names as synonyms, and also keep these old names as documented if applicable. - There are functions `BaseIntMat`, `TriangulizeIntegerMat`, etc. Introduce the corresponding names `BaseIntegerMatrix`, `TriangulizeIntegerMatrix`, etc. as main names, keep the `IntMat` or `IntegerMat` names as synonyms, and also keep the old names as documented if applicable. - Rename *RowVector to *Vector like AddRowVector to AddVector - [ ] rename functions like `RankMat` consistently to `RankMatrix`; make sure to provide synonyms for the old name, to retain backwards compatibility - There is a problem with the operation `Matrix`. An *attribute* with this name is declared for certain semigroups, in `lib/reesmat.gd`. In `lib/matobj2.gd`, `Matrix` is declared as a *mutable attribute* for lists; if it would not be declared as such then GAP's standard machinery would cause that `Matrix( <list> )` returns an *immutable* value, which is not acceptable. The declaration as a mutable attribute in `lib/matobj2.gd` works because this file is read before `lib/reesmat.gd`, but the consequence is that the attribute values in the semigroup context are also mutable (try the manual example for `Matrix`); this is a bug. (One could turn any attribute into a mutable one, by adding a corresponding declaration early enough.) #### Done - [x] add support for `mat[i,j]` to access matrix entries (the syntax is already supported, this is mostly about installing suitable methods) - [x] perhaps also allow this for plain "lists of lists", to make it easier to write code which accepts both lists of lists and MatrixObj? - Replace `PositionNot( obj, zero )` by `PositionNonZero( obj )`. (And change the default methods for `PositionNonZero`.) (done) ### Adapting the packages - enhance [`cvec`](https://github.com/gap-packages/cvec) to properly implement MatrixObj/VectorObj - the `cvec` package has an alternative implementation of compressed vectors and matrices - it is based on an older version of the MatrixObj spec - it may also be useful to turn off support for `mat[i]` for type cmat, at least temporarily, to find places where it still accesses that - [ ] [`meataxe64` ](https://github.com/gap-packages/meataxe64) - [ ] Teach `recog` to use `MatrixObj` ### Improvements of exisiting MatrixObj code - [ ] Go through all the operations/attributes in `matobj2.gd`; then, if we - decided to keep the... - [ ] write a proper documentation comment; - [ ] verify that a default implementation has been provided (if applicable), else provide one; - [ ] write some tests - [ ] possibly provide optimized versions for plistrep, 8bit rep, GF2 rep, - For the various constructors, perhaps imitate what we do for e.g. group constructors: Allow to omit the filter, and try to choose a "good" default representation? - Replace `ChangedBaseDomain` (which is defined for both vector and matrix objects) by `VectorWithChangedBaseDomain`, `MatrixWithChangedBasedDomain`. - Is the ordering of arguments sensible and consistent? (For example, better define `CopySubMatrix( dst, drows, dcols, src, srows, scols )`, and unify the argument order in `NewMatrix( filt, R, ncols, list )` and `Matrix( filt, R, list, ncols )`? And what about `NullMat`/`ZeroMatrix` and `IdentityMat`/`IdentityMatrix`?) - Are the argument names sensible and consistent? For example we sometimes write "rep" and sometimes "filt" for the same thing. And we sometimes write "rows, colss", other times "ncols", or "m,n", or... - Are the operations for vector and matrix objects consistent? #### Done - [x] `RowLength` seems like a really bad idea. I propose that instead we add `NumberRows` and `NumberColumns` to the MatrixObj API and ditch `RowLength`. Ideally I'd also disallow `Length` for MatrixObj, but perhaps it's useful to have it (as alias for NumberRows???) to make transitioning code to using MatrixObj easier. _But_ I think it would be better to start without it, and see whether we really can benefit from it. I.e. show me code that becomes trivially able to use MatrixObj because of Length, and we can keep it... but if you have to modify the code anyway, I think changing it to use `NumberRows` is better * [x] some love for `foo[i,j]` syntax * [x] benchmark it via `foo[i][j]` resp. `MatElm(foo,i,j)` * [x] look into replacing the existing non-optimized dispatch code for `foo[i,j]` by something that is faster in special cases: Right now, `foo[i,j]` translates to `foo[ [i,j] ]` (so a temporary plist is created), and then we dispatch generically to methods for `\[\]` resp. `\[\]:=`; we could avoid creating the temporary plist, and instead dispatch through `MatElm` resp. `SetMatElm` * [x] ensure `foo[i,j]` also works for plists-of-plists resp. for `IsMatrix`; and in addition, see about extending the hot path from above for this, i.e. don't even dispatch through `(Set)MatElm`, but directly modify the list; * [x] perhaps also have hot paths for compressed matrices??? ### Tests - Create generic "parametrized" tests, see [this issue](https://github.com/gap-system/gap/issues/4512) - Go through GAP library functions for matrices and test each of them with a "strict" MatrixObj implementation, and fix them as needed - e.g. try to create a group of MatrixObj - or try to use the meataxe with such matrices and fix it - or try to use the recog package with them - once/if a function "works" with proper MatrixObj, add a test which verifies this - relevant issues: [don't force MatrixObj to be collections?](https://github.com/gap-system/gap/issues/3568) ### Documentation - [ ] Complete the documentation, see http://www.gap-system.org/Manuals/doc/ref/chap26.html - add entry in programming language section of ref manual for x:=A[B]; and A[B]:=x; for arbitrary objects B - Document the interface. - add entries for [] and []:= to MatrixObj chapter. link to these from the list chapter entries for [] and []:= (and vice versa) - Add a chapter `How to use IsMatrixObj` to the documentation. There, add the following sections to the documentation: - which Methods should be implemented for new IsMatrixObj types so that the generic methods work for all new objects - list the generic methods in `matobj.gi`. Say that these can/should be overwritten by more efficient specialized code. - Changes from IsMatrix to IsMatrixObj - how to convert existing IsMatrix code to adapt IsMatrixObj - deprecated usages of operations/functions - The entry for "unbind a list entry" in the Reference Manual is syntactically wrong, it is shown as `Unbind( list[, n] )`, which means that `n` is an optional argument. The problem is that GAPDoc interprets `Arg="list[n]"` this way, and inserts the comma. - Document `PostMakeImmutable`, then mention that matrix objects may have to install methods for it. - How is `WeightOfVector` in the MatrixObj Chapter related to `WeightVecFFE`, `DistanceVecFFE`? Should just `WeightOfVector` be documented? ### Nice to have - Would it be sensible to have a recursive version of `ShallowCopy`, with an optional argument that limits the depth? (Note that `StructuralCopy` is not an operation.) `MutableCopyMatrix` (which is defined and used in `lib/matrix.g*` but is not documented) could then be replaced by a depth 2 call of that operation. On the other hand, the name `MutableCopyMatrix` is quite suggestive, perhaps `MutableCopyVector` would be a better name for `ShallowCopy`.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully