Building the Q# compiler in mac os

Earlier this year Microsoft open sourced a number of their quantum programming tools including the compiler for the qsharp(q#) quantum programming language. I really enjoy reading and learning from open source software, so the news were really exciting for me since it allowed to look at the internals of qsharp.

The following post summarizes some of my experiences working and building the qsharp compiler code base as member of the open source comunity.

My main dev environment is macOS so lots of the instructions here will be targeted for it, however if your are running a *nix environment is pretty much same.

Prerequisites

Here is the list of things you are going to need to build the qsharp compiler.

  • Dotnet

    $> dotnet --version
    3.0.101
    
  • Power shell

    $> brew cask install powershell
    ...
    $> pwsh --version
    PowerShell 6.2.3
    
  • Qsharp’s source code

    $> git clone https://github.com/microsoft/qsharp-compiler.git
    
  • bsondump and jq To visualize the compiler output. I found it surprinsing that there was no easy way to install a bson->json converter so I decided to build a mongodb tool from the source above (FYI you’ll need the go tool chain for that)

  • A text editor of your choice. Here life starts getting a little complicated for macOS users. Vim, usually my editor of choice, sadly struggled to support symbolic navigation, specially cross-project. Visual Studio Code, fine choice for most projects, and an excellent choice for C# projects, did not have a good F# plugin. I settled for Visual Studio for macOS, even though it lacks some pretty key text editor plugins (vim style emulation, for example). On the plus side I found that Visual Studio offered the best experience when navigating a mix C#, F# code base.

Building

  • Move to your the qsharpcompiler repository and execute the boostraping script
$> cd qsharp-compiler
$> pwsh bootstrap.ps1
...
Enable telemetry: false
Adding Newtonsoft.Json.Bson
Adding Microsoft.VisualStudio.LanguageServer.Protocol
Adding FSharp.Core
Adding System.ValueTuple
Adding Newtonsoft.Json
Adding System.Collections.Immutable
Adding Markdig
Adding YamlDotNet
Adding FParsec
Adding Microsoft.CodeAnalysis.CSharp

dependency
----------
{dependency, dependency, dependency, dependency…}
  • Build the compiler project
$> dotnet build QsCompiler.sln
Microsoft (R) Build Engine version 16.3.2+e481bbf88 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
....

Make sure you have sync’d your repo with the lastest qsharp repository. I fixed a few minor issues with the build script in macOS and they relatively new commits.

If everything worked fine, you now should be able to call the qsharp compiler from its command line tool.

$> src/QsCompiler/CommandLineTool/bin/Debug/netcoreapp3.0/qsc
Microsoft Q# compiler command line tool. 1.0.0
© Microsoft Corporation. All rights reserved.

ERROR(S):
  No verb selected.

  build       Builds a compilation unit to run on the Q# quantum simulation framework.

  diagnose    Generates intermediate representations of the code to help diagnose issues.

  format      Generates formatted Q# code.

  help        Display more information on a specific command.

  version     Display version information.

$> src/QsCompiler/CommandLineTool/bin/Debug/netcoreapp3.0/qsc version
Microsoft Q# compiler command line tool. 1.0.0

Running it

With a built compiler we are ready to take for a spin. The command line tool of the qsharp compiler, provides a few options to specify input. For example we can pass a snippet of q# code like so:

$> src/QsCompiler/CommandLineTool/bin/Debug/netcoreapp3.0/qsc build -s "var uf = 2;"

Error QS3034:
File: /Users/eginez/repos/qsc/__CODE_SNIPPET__.qs
Position: [ln 1, cn 5]
Unexpected code fragment.

Error QS3035:
File: /Users/eginez/repos/qsc/__CODE_SNIPPET__.qs
Position: [ln 1, cn 1]
An expression used as a statement must be a call expression.

____________________________________________

Q# compilation failed: 2 errors, 0 warnings


$> src/QsCompiler/CommandLineTool/bin/Debug/netcoreapp3.0/qsc build -s "mutable uf = 2;"
____________________________________________

Q#: Success! (0 errors, 0 warnings)

Of course you probably want to save the source code to a file and pass that file to the compiler, for that you can do

$> cat sample.qs
namespace blank {
        operation OneQ () : Unit {
        using(q = Qubit()) {
        }
    }
}
$> src/QsCompiler/CommandLineTool/bin/Debug/netcoreapp3.0/qsc build -i sample.qs -o out/
____________________________________________

Q#: Success! (0 errors, 0 warnings)

$>ls out
twnlm5ty.bson twnlm5ty.dll

The output of the compiler is both a bson file and dll with the bson in it. Finally you can look at the output of the compiler with the bsondump tools

$> $GOPATH/mongodb/mongo-tools/bin/bsondump twnlm5ty.bson|jq
...
"Statements": [
                          {
                            "Statement": {
                              "Case": "QsQubitScope",
                              "Fields": [
                                {
                                  "Kind": {
                                    "Case": "Allocate"
                                  },
                                  "Binding": {
                                    "Kind": {
                                      "Case": "ImmutableBinding"
                                    },
                                    "Lhs": {
                                      "Case": "VariableName",
                                      "Fields": [
                                        "q"
                                      ]
                                    },
                                    "Rhs": {
                                      "Case": "SingleQubitAllocation"
                                    }
                                  },
                                  "Body": {
                                    "Statements": [],
                                    "KnownSymbols": {
                                      "Variables": [
                                        {
                                          "VariableName": "q",
                                          "Type": {
                                            "Case": "Qubit"
                                          },
                                          "InferredInformation": {
                                            "IsMutable": false,
                                            "HasLocalQuantumDependency": false
                                          },
                                          "Position": {
                                            "Case": "Value",
                                            "Fields": [
                                              {
                                                "Item1": {
                                                  "$numberInt": "1"
                                                },
                                                "Item2": {
                                                  "$numberInt": "8"
                                                }
                                              }
                                            ]
                                          },
                                          "Range": {
                                            "Item1": {
                                              "Line": {
                                                "$numberInt": "1"
                                              },
                                              "Column": {
                                                "$numberInt": "7"
                                              }
                                            },
                                            "Item2": {
                                              "Line": {
                                                "$numberInt": "1"
                                              },
                                              "Column": {
                                                "$numberInt": "8"
                                              }
                                            }
                                          }
                                        }
                                      ],
....

As you can see the compiler outputs the whole AST and metadata in json format.

The compiler code itself is split into a number of packages, some interesting packages to look at are: Optimizations, SyntaxProcessor, Compilation Manager. In addition the compiler project has a number of targets, for example: CommandLineTool, LanguageServer and the Tests targets. The CommandLineTool and the Test targets are the most relevent and as we saw built with no problems, the Test target still has a build problem on macOS I haven’t tracked down yet.

Error handling in golang, an experience report

In the spirit of contributing to the golang community, I would like to document my experience while trying to make error handling a little more palatable.

Error handling sits high among the things that I would like to do better in go, thus while working on a recent project I decided to invest some time looking into how to improve.

I’ll start by showing code snippets of what I thought could have been improved:

//Loads the data structures
func Load(root string, keyRing string, p pgp.PromptFunction) (index *MeerkatsIndex, key *MeerkatsKeys, e error) {

	//Load keys
	index = &MeerkatsIndex{}
	key = &MeerkatsKeys{}
	fr, e := os.OpenFile(filepath.Join(root, ".keys"), os.O_RDWR, os.FileMode(0700))
	e = key.Read(fr)
	if e != nil {
		return
	}

	//Load index
	fr, e = os.OpenFile(filepath.Join(root, ".index"), os.O_RDWR, os.FileMode(0700))
	if e != nil {
		return
	}
	ent, entList, e := LoadKeysFromDisk(keyRing, p, key)
	if e != nil {
		return
	}

	e = index.Read(fr, ent, entList)
	return
}

That is a short of example showcasing something pretty obvious: there is a lot repetitive error handling pretty much doing the same thing. My first intuition was to replace all the error checking statements with a function that would check for the status of an error variable and then just panic.

//check for errors
func logAndExitIfError(e error) {
	if e != nil {
		panic(e)
	}
}

func EncryptPipeline(to pgp.EntityList, from *pgp.Entity, secret []byte) (encbytes []byte, e error) {
	e = nil
	plain := bytes.NewBuffer(nil)
	zipped := gzip.NewWriter(plain)
	armored, e := armor.Encode(zipped, "PGP MESSAGE", nil)
	logAndExitIfError(e)
	encrypted, e := pgp.Encrypt(armored, to, from, nil, nil)
	logAndExitIfError(e)

	_, e = encrypted.Write(secret)
	logAndExitIfError(e)
	//...
}

That works pretty well if all I want to do is exit the program, but for the cases where I needed more than just exiting, this technique does not work.

Still unsatisfied with the state of affairs I went looking for answers in the internet. I started by looking at other people’s code too see how they have been solving the same problem. Surprisingly, I found that the vast majority of projects I looked into were basically if-checking on every error.

That seems less than ideal to me. And as far as I can tell it is a source of complaint amongst gophers. So much so that Rob Pike himself wrote a [post] (https://blog.golang.org/errors-are-values) on this specific topic a couple of years ago. In it, he describes a clever technique to deal with errors. He makes the point that errors should be treated as values (which they should) and goes on to provide an example on how to program around errors. I won’t describe his whole solution however I’d like to highlight a key part of his example. Consider the following interface and function:

type errWriter struct {
    w   io.Writer
    err error
}

func (ew *errWriter) write(buf []byte) {
    if ew.err != nil {
        return
    }
    _, ew.err = ew.w.Write(buf)
}

Give the above, and as explained in Rob’s post, you could write a function to use the above solution, roughly looking like so:

...
ew := &errWriter{w: fd}
ew.write(..)
ew.write(..)

// and so on
if ew.err != nil {
    return ew.err
}

At a first glance this is great solution for the problem. The error checking logic is localized to a single function and the consumer only has to worry about checking the error when the function is about to return. However, when I tried to extend this solution to work on more than just one use case, I started running into some problems.

The first thing I noticed is that one might want to do more than just call ew.write. In fact, ideally, we should be able to handle arbitrary error throwing functions.

Closures seem like the right tool for that. Our new error check structure would like so:

type ErrorCatcher struct {
	err error
}

func (ec *ErrorCatcher) TryFile(fn func() (*os.File, error)) (ret *os.File) {
	if ec.err != nil {
		return nil
	}

	ret, ec.err = fn()
	return ret
}

Great, we now have a function called TryFile. It will execute an error throwing closure if there are no prior errors. Thus we can now can do something like this:

func possibleErrorThrowingFunction() (error) {
	erc := ErrorCatcher{}
	file1 := erc.TryFile(func() (*os.File, error) {
		return os.Open("/some/fine/file.txt")
	})

	file2 := erc.TryFile(func() (*os.File, error) {
		return os.Open("/some/fine/file.txt")
	})
    
    return erc.err
}

In the spirit of contributing to the golang community, I would like to document my experience while trying to make error handling a little more palatable.

Error handling sits high among the things that I would like to do better in go, thus while working on a recent project I decided to invest some time looking into how to optimize error checking.

I’ll start by showing code snippets of what I thought could have been improved.

//Loads the data structures
func Load(root string, keyRing string, p pgp.PromptFunction) (index *MeerkatsIndex, key *MeerkatsKeys, e error) {

	//Load keys
	index = &MeerkatsIndex{}
	key = &MeerkatsKeys{}
	fr, e := os.OpenFile(filepath.Join(root, ".keys"), os.O_RDWR, os.FileMode(0700))
	e = key.Read(fr)
	if e != nil {
		return
	}

	//Load index
	fr, e = os.OpenFile(filepath.Join(root, ".index"), os.O_RDWR, os.FileMode(0700))
	if e != nil {
		return
	}
	ent, entList, e := LoadKeysFromDisk(keyRing, p, key)
	if e != nil {
		return
	}

	e = index.Read(fr, ent, entList)
	return
}

That is a short example showcasing something obvious: there is a lot repetitive error handling doing the same thing. My first intuition was to replace all the error checking statements with a function that would check for the status of an error variable and then panic.

//check for errors
func logAndExitIfError(e error) {
	if e != nil {
		panic(e)
	}
}

func EncryptPipeline(to pgp.EntityList, from *pgp.Entity, secret []byte) (encbytes []byte, e error) {
	e = nil
	plain := bytes.NewBuffer(nil)
	zipped := gzip.NewWriter(plain)
	armored, e := armor.Encode(zipped, "PGP MESSAGE", nil)
	logAndExitIfError(e)
	encrypted, e := pgp.Encrypt(armored, to, from, nil, nil)
	logAndExitIfError(e)

	_, e = encrypted.Write(secret)
	logAndExitIfError(e)
	//...
}

That works pretty well if all I want to do is exit the program. Still unsatisfied with the state of affairs I went searching for answers in the internet. I started by looking at other people’s code too see how they have been solving the same problem. Surprisingly, I found that the vast majority of projects I looked into were basically if-checking on every error. That seems less than ideal to me.

As far as I can tell it is a source of complaint amongst gophers. So much so that Rob Pike himself wrote a [post] (https://blog.golang.org/errors-are-values) on this specific topic a couple of years ago. In it he describes a clever technique to deal with errors. He makes the point that errors should be treated as values and goes on to provide an example on how to program around errors. I won’t describe his whole solution however I’d like to highlight a key part of his example. Consider the following interface and function:

type errWriter struct {
    w   io.Writer
    err error
}

func (ew *errWriter) write(buf []byte) {
    if ew.err != nil {
        return
    }
    _, ew.err = ew.w.Write(buf)
}

As Rob’s post explains, you could write a function using the above solution, which would look something like this:

...
ew := &errWriter{w: fd}
ew.write(..)
ew.write(..)

// and so on
if ew.err != nil {
    return ew.err
}

At a first glance this is great solution for the problem. The error checking logic is localized to a single function and the consumer only has to worry about checking the error when the function is about to return. However, when I tried to extend this solution to work on more than just one use case, I started running into some problems.

The first thing I noticed is that one might want to do more than just call ew.write. In fact, ideally, we should be able to handle arbitrary error throwing functions.

Closures seem like the right tool for that. Our new error check structure would like

type ErrorCatcher struct {
	err error
}

func (ec *ErrorCatcher) TryFile(fn func() (*os.File, error)) (ret *os.File) {
	if ec.err != nil {
		return nil
	}

	ret, ec.err = fn()
	return ret
}

Great, we now have a function called TryFile. It will execute an error throwing closure if there are no prior errors. Thus we can now can do something like this:

func possibleErrorThrowingFunction() (error) {
	erc := ErrorCatcher{}
	file1 := erc.TryFile(func() (*os.File, error) {
		return os.Open("/some/fine/file.txt")
	})

	file2 := erc.TryFile(func() (*os.File, error) {
		return os.Open("/some/fine/file.txt")
	})
    
    return erc.err
}

Other than the verbosity of the closure declaration, this accomplishes what we need if all we needed to return was of type File.A common technique to generlize for a type is to use the interface{} type. Replacing the concrete type with go’s interface{} would look like this:

func (ec *ErrorCatcher) TryAny(fn func() (interface{}, error)) interface{} {
	if ec.err != nil {
		return nil
	}

	var ret interface{}
	ret, ec.err = fn()
	return ret
}

We can now utilize our error checking structure much more freely, like so:

func GetFleInfo() (info os.FileInfo, err error) {
	erc := ErrorCatcher{}
	file := erc.TryAny(func() (interface{}, error) {
		return os.Open("/some/fine/file.txt")
	}).(*os.File)

	fileInfo := erc.TryAny(func() (interface{}, error) {
		return file.Stat()
	}).(os.FileInfo)

	return fileInfo, erc.err
}

A few observations:

  • We now have to type assert the return value. This seems silly since the return type is well known in all cases (just not the same). Why can’t go let me express that!? Falling back to a type that expresses no information hardly seems like the right solution.
  • The above fragment is marginally better than the manual error checking fragment, mostly due to the typing of the closure argument but Go could infer the types of a function after all, go’s compiler can already infer variable’s types, why not function’s type? It will cut down on a lot of typing.

In my opinion, the above experience highlights the importance of generics. Having some way to express an arbitrary type is very useful when trying to express solutions to problems like the one above.

func (ec *ErrorCatcher) TryAny(fn func() (`T`, error) `generic: T` {
...
...
    return fn()
}