Principal Software Engineer at Allscipts in Malvern, Pennsylvania, Microsoft Windows Dev MVP, Husband, Dad and Geek.
61649 stories
·
22 followers

Video: A Tour of Go for the C# Developer

1 Share

I've found a lot of interesting features and ideas in Go (golang). By exploring these, I've got several things to try out in my primary language (which is currently C#).

If you're curious about Go, I've put together a tour of the language targeted at the C# developer. By building an application that makes concurrent service calls, we can see some of the similarities and differences between C# and Go.

Watch it on YouTubeA Tour of Go (golang) for the C# Developer

There's also a repo with all of the code: https://github.com/jeremybytes/go-for-csharp-dev

Features

Here are a few of the things I find interesting about Go:

Opinionated Syntax
Go syntax is C-like, but it has some strong opinions. For example, opening curly braces must always go on the same line as the function (or "if", or other block statement). In addition, the curly braces are never optional for an "if" statement, regardless of how many lines are part of the body.

No Unused Variables
Go does not allow for unused variables. If you declare a variable and do no use it, you get a compiler error. I like this idea since it gets rid of code that isn't used. This also encourages the use of the "blank identifier" (an underscore) which acts as a discard.

Baked-in Concurrency
Concurrency is baked right into the Go language -- in fact, they are called "goroutines" and you use "go" to start one. Channels can be used to communicate between a goroutine and other functions. Seeing how they work makes the idea of Channels in C# (added in .NET Core 3.0) a bit more interesting to explore.

Deferred Calls
Go has a "defer" statement. For example, "defer channel.Close()" will call the "Close()" method at the end of the function (however it exits). This is similar to a "finally" in C#, but it can be used anywhere in a function or method.

Error Handling
Go encourages the the use of "error" instead of exception (which is called "panic" in Go). An error is really just a message. A common pattern is for a function to return a data item plus an error. If the error is populated, then something went wrong. The idea is that we treat errors as messages to be handled in our own code rather than something to be handled by the language or framework. (And "panic" does exist if we have a truly panic-inducing circumstance.)

Multiple Return Values
As mentioned above, it's common for a function to return a data item plus an error. Go supports multiple return values. So we can return multiple values without dealing with explicit tuples.

Interfaces
Interfaces in Go are explicitly declared but implicitly implemented. Meaning, if we have an interface that declares a "String()" method, a type can implement that the interface by simply providing a "String()" method. It does not need to specifically say it implements the interface, it just needs to provide the members. This encourages a different way of using defining and using interfaces. I wrote a bit about my initial experiences here: Go and Interfaces.

Check Out the Video

In building a small application (about 100 lines), the video looks at all of these features, plus a few more. Be sure to check it out:

Happy Coding!

Read the whole story
alvinashcraft
5 hours ago
reply
West Grove, PA
Share this story
Delete

Blazor WebAssembly vs Angular: Client Side Clash

1 Share
Which SPA technology should you use: Blazor WebAssembly or something more mature like Angular, and why?
Read the whole story
alvinashcraft
5 hours ago
reply
West Grove, PA
Share this story
Delete

Introduction to Functional Programming in F# - Table of Contents

1 Share

Introduction

This series of posts will introduce you to the world of functional programming (FP) in F#. Rather than start with theory or formal definitions, we will look at some typical business problems and explore how we can use some of the exciting functional programming features in F# to solve them.

No previous experience of functional programming or F# are required.

Post Listing

  • Part 1 -> A worked example showing a few exciting F# features
  • Part 2 -> Function composition
  • Part 3 -> Option and Result modules
  • Part 4 -> More function composition and unit tests
  • Part 5 -> Collections and immutability
  • Part 6 -> Sequence expressions
  • Part 7 -> Active patterns
  • Part 8 -> Using active patterns for validation
  • Part 9 -> Type alias and single case discriminated union
  • Part 10 -> Object programming
  • Part 11 -> Recursion
  • Part 12 -> Computation expressions

More Reading

Read the whole story
alvinashcraft
5 hours ago
reply
West Grove, PA
Share this story
Delete

A Productive Aurelia 2 Build Setup

1 Share

When starting a new Aurelia 2 project I have a few “musts” that I always setup. These Build Setup tweaks are adding functionality as well as quality of life things. ๐Ÿ‘จโ€๐Ÿญ

My preferred setup for my Aurelia 2 projects are based on the Dumber bundler. The build config is setup in gulp, and easily configurable without proprietary tool knowledge. Ok – I admit, some gulp knowledge helps ๐Ÿ˜‚

An Aurelia 2 project build setup using dumber as bundler
Dumber FTW๐Ÿฅ‡

My standard project setup options look like follows:

  • Bundler – Dumber
  • Transpiler – TypeScript
  • Shadow DOM or CSS Modules – No (varies depending on app)
  • CSS Processor – Plain CSS
  • Unit Testing Framework – Jest
  • E2E Tests – Cypress
  • Sample Project – With Direct Routing (varies depending on app)

If preferred, this exact same project setup can be created in silent mode like this:
npx makes aurelia new-project-name -s dumber,typescript,cypress,app-with-router

๐ŸŽ Additions

After setting up a new project, my tweaks and additions are:

  1. Add a resources folder to hold images, json files etc
  2. Tweak glob pattern for other files that trigger rebuilds in watch mode
  3. Add PurgeCSS and add it to the gulp buildCSS function

๐Ÿ“‚ A Folder for Images, Config Files Etc.

I add a folder in the root next to src named resources. In this folder I store pretty much everything that is not code or markup.

Since I store config files and such in here that changes how the app behaves, I also add this folder to the gulp watch glob. This enables changes to files in the folder to trigger new builds when developing in watch-mode.

Modify gulpfile.js and add a glob pattern, se below.

function watch() {
  return gulp.watch(["src/**/*", "resources/**/*"], gulp.series(build, reload));
}

๐Ÿ‘€ Another File to Add to the Watch Glob

Another file I end up modifying, at least in the beginning of every project, is the _index.html template. Might as well add it to the watch glob! ๐Ÿ˜

function watch() {
  return gulp.watch(["src/**/*", "resources/**/*", "_index.html"], gulp.series(build, reload));
}

๐Ÿงน Add PurgeCSS to the Build Setup

A great tool for removing obsolete CSS is PurgeCSS. If you are using any third party styling frameworks like Bootstrap, TailwindCSS or something else, the you probably are shipping loads of unused styles to your app visitors. PurgeCSS will help you remove all those unused styles ๐Ÿฅณ

Even if it’s a small project and you write all your CSS yourself, it’s easy to miss something and let some dead CSS slip through the cracks every now and then ๐Ÿ˜ต

Install PurgeCSS using npm
npm i -D gulp-purgecss

Then modify the buildCSS() function in the gulpfile.js like this๐Ÿ‘‡

// add this at the top of the file
const purgecss = require("gulp-purgecss");

// then we add PurgeCSS to the CSS build
function buildCss(src) {
  return gulp
    .src(src, { sourcemaps: !isProduction })
    .pipe(
      // add PurgeCSS to remove unused CSS
      purgecss({
        content: ["src/**/*.html", "_index.html"],
        whitelist: ["goto-active"],
      })
    )
    .pipe(
      postcss([
        autoprefixer(),
        // use postcss-url to inline any image/font/svg.
        // postcss-url by default use base64 for images, but
        // encodeURIComponent for svg which does NOT work on
        // some browsers.
        // Here we enforce base64 encoding for all assets to
        // improve compatibility on svg.
        postcssUrl({ url: "inline", encodeType: "base64" }),
      ])
    );
}

Notice we need to add goto-active to the whitelist, as it’s a dynamic class put on navigation elements by the router, and otherwise that class will be removed.

๐Ÿ”ฎ Conclusion

Having a good DEV environment setup is key for a good flow and productivity. Make sure to think about the build setup as a part of that too! Don’t stay complacent when you feel pain points – modify, tweak and improve! ๐Ÿฅฝ

Do you have any great productivity or functionality adding tweaks?
Comment below and share your best tips! ๐Ÿค—

๐Ÿ”— Resources

Example code can be found on GitHub
Dumber docs
PurgeCSS home
Using VS Code to stamp out Aurelia components
Read more about Aurelia 2 on theย docsย site

Happy Coding! ๐Ÿ˜Š

The post A Productive Aurelia 2 Build Setup appeared first on mobilemancer.

Read the whole story
alvinashcraft
5 hours ago
reply
West Grove, PA
Share this story
Delete

Finishing the Project Template IWizard

1 Share

After our total failure in the last stream, I reached out to Microsoft again and explained the issue we ran into. Turns out, it was indeed a big! YAY! It wasn’t my code. David Kean, at Microsoft, gave me another work around to try while they fix the bug. IN this stream, we implement that […]

The post Finishing the Project Template IWizard appeared first on Brian Lagunas.

Read the whole story
alvinashcraft
5 hours ago
reply
West Grove, PA
Share this story
Delete

Running Q# compiler and simulation programmatically from a C# application

1 Share

The QDK provides an excellent, low barrier way of getting started with Q# development – without having to deal with the compiler directly, or worrying about how to simulate the code you wrote on a classical device. Additionally, for more technically versed users, the Q# compiler is also available as a command line utility that can be used to fine tune the compilation experience and cater to complex scenarios. The QDK is well documented, and the command line compiler provides good documentation as part of the application itself, but one of the things that is not widely known is that the Q# compiler can also be easily used programmatically – via its Nuget package.

Let’s have a look.

Sample Q# code

Let’s imagine the following – fairly basic – piece of Q# code that we’ll use as our testbed.

namespace HelloQuantum {

    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Measurement;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Convert;

    @EntryPoint()
    operation HelloQ() : Unit {
        let ones = GetRandomBit(100);
        Message(""Ones: "" + IntAsString(ones));
        Message(""Zeros: "" + IntAsString((100 - ones)));
    }
    
    operation GetRandomBit(count : Int) : Int {
 
        mutable resultsTotal = 0;
 
        using (qubit = Qubit()) {       
            for (idx in 0..count) {               
                H(qubit);
                let result = MResetZ(qubit);
                set resultsTotal += result == One ? 1 | 0;
            }
            return resultsTotal;
        }
    }
}

The code above contains a Q# entry point operation that will allow for a creation of a standalone Q# application. The entry operation invokes another operation which internally will borrow a single qubit, apply the Hadamard transformation on it and measure the qubit in the Pauli Z axis, repeating this a 100 of times. Finally, the amount of classical zero and one values obtained from the measurement will be printed out. The usage of Hadamard here means that we are expecting a roughly equal distribution of zeroes and ones as that is guaranteed by the mathematical formalism of quantum mechanics.

Additionally, it’s worth mentioning that we use helper functions from various Q# namespaces – such as the MResetZ or IntAsString, all of which must be opened first, hence the appropriate statements up top.

Now, instead of using the regular QDK process of building a quantum application and then simulating it on top of the .NET runtime using dotnet run, we will build our own program that will make use of the Q# compiler and emit the C# code that can be used for simulation. Then, we will compile this C# code and run it to simulate our quantum application.

Embedding the Q# compiler into a C# application

One of the great value propositions of Roslyn, the C# compiler, is that it can be used as a so called compiler-as-a-service. This means that it is possible to incorporate the compiler into your own program, and use the compiler’s pipeline to build your own features and to easily compile code on demand. We will show here today that the Q# compiler can also be used in a similar way – which I personally find very attractive.

In order to add a Q# compiler to a C# application, we need to add a package reference to the Microsoft.Quantum.Compiler Nuget package. That package, just like the rest of the Microsoft quantum packages are versioned together with the QDK, so the latest compiler version is actually the same version as the latest QDK that you might read about in the announcement posts. For example, at the time of writing, the current QDK version is 0.12.20072031, which means all of the related packages we shall use will have the same version. This makes it quite easy to understand how different packages relate to each other; this is particularly useful in case you pull in several quantum packages at once – which we will do here. We are going to reference the packages responsible for C# generation, simulation and entry point generation.

On top of that we will also add a reference to the C# compiler – we will use the C# Roslyn compiler to compile the emitted C# code. At the time of writing, the latest Roslyn version on Nuget is actually 3.7.0.4-final, but we will use version 3.6.0 instead. That is the version that is used by the Q# to C# code generation package 0.12.20072031 and we want to avoid compiler warning about mismatching versions of Roslyn.

Overall, the project file for our C# application looks as follows:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.6.0" />
    <PackageReference Include="Microsoft.Quantum.Compiler" Version="0.12.20072031" />
    <PackageReference Include="Microsoft.Quantum.Simulators" Version="0.12.20072031" />
    <PackageReference Include="Microsoft.Quantum.CsharpGeneration" Version="0.12.20072031" />
    <PackageReference Include="Microsoft.Quantum.EntryPointDriver" Version="0.12.20072031" />
  </ItemGroup>

</Project>

Invoking the Q# compiler programmatically

The process that we are about to go through is the following:

  1. get ahold of some Q# code – this we prepared already
  2. prepare assembly references necessary to compile the Q# code
  3. prepare the Q# compiler so that it would include a rewrite step that would rewrite the Q# syntax trees into C# code
  4. invoke the Q# compiler and if no error diagnostics were produced, capture the created C# output
  5. prepare assembly references necessary to compile the created C# code
  6. compile the C# code and if no error diagnostics were produced, emit into an in-memory assembly
  7. invoke the in-memory assembly to execute our simulated Q# program

Let’s look at the steps one by one. First, we will – for simplicity – start by holding our Q# code in a C# string variable.

// sample Q# code
    var qsharpCode = @"
namespace HelloQuantum {

    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Measurement;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Convert;

    @EntryPoint()
    operation HelloQ() : Unit {
        let ones = GetRandomBit(100);
        Message(""Ones: "" + IntAsString(ones));
        Message(""Zeros: "" + IntAsString((100 - ones)));
    }
    
    operation GetRandomBit(count : Int) : Int {
 
        mutable resultsTotal = 0;
 
        using (qubit = Qubit()) {       
            for (idx in 0..count) {               
                H(qubit);
                let result = MResetZ(qubit);
                set resultsTotal += result == One ? 1 | 0;
            }
            return resultsTotal;
        }
    }
}";

Next, we shall locate the necessary Q# assemblies required for this code to compile. Based on the Q# types in use, that would actually only be Microsoft.Quantum.QSharp.Core and Microsoft.Quantum.Runtime.Core, however depending on the complexity of the Q# code you are trying to compile and the nature of computations done, additional libraries may of course be required.

Since our app already references Microsoft.Quantum.Simulators, and that package contains both of the above mentioned assemblies as dependencies, we can locate them in our application already by assembly name and find the physical location that way. However, it would be equally valid to just hardcode the paths to those DLLs or use any other DLL location mechanism that would suit you. Most important is, that we have a list of reference locations that we can hand off to the Q# compiler.

// necessary references to compile our Q# program
var qsharpReferences = new string[]
{
    "Microsoft.Quantum.QSharp.Core",
    "Microsoft.Quantum.Runtime.Core",
}.Select(x => Assembly.Load(new AssemblyName(x))).Select(a => a.Location);

Next we will enable additional tracing in the Q# compiler. This is technically not necessary, but it will give us a better overview of what is going on in the compiler when it gets invoked. This is – unfortunately – at the moment only possible to do statically. We shall log the received events to the console and be able to follow the compiler’s progress that way.

// events emitted by the Q# compiler
CompilationLoader.CompilationTaskEvent += (sender, args) =>
{
    Console.WriteLine($"{args.ParentTaskName} {args.TaskName} - {args.Type}");
};

Before we move towards the next step, we need to quickly provide a short piece of background information about how the Q# compiler works under the hood. The compiler exposes an interesting extensibility point called rewrite steps. It allows you to provide your own custom logic – compilation steps – that get executed during the compilation process. The QDK actually ships with a dedicated assembly, Microsoft.Quantum.CsharpGeneration which contains a rewrite step called CsharpGeneration (interestingly the C# generation code in this case is actually written in …F#). That step is responsible for taking the Q# code and translating it into C# code that could run and simulate the quantum behavior. All of that machinery is bootstrapped by the QDK and as a user you typically do not have to worry about that – but indeed, at its core, it is that rewrite step that allows you to execute your Q# code on your non-quantum development machine when you invoke dotnet run from the command line. The entire rewrite step infrastructure is very interesting and we shall definitely return to it in the future posts.

What we will want to do in our sample though, is we will not use the “official” CsharpGeneration rewrite step (the one that the QDK automatically uses behind the scenes) but instead provide our own. The reason behind this is that the official generation step, while it generates a perfectly valid C# that can be used to simulate the Q# application, explicitly writes out the output to text files located on the file system. What the QDK then does, it actually picks them up from your hard drive and continues from there. In our sample, however, we’d like to keep everything in process and therefore we will simply capture the C# output into in-process state, rather than having to worry about performing the unnecessary file I/O.

I will not go into much of the details of writing rewrite steps for the Q# compiler – as mentioned, this warranties its own dedicated blog post. It is enough to say that we will implement the IRewriteStep interface, where the core functionality is to provide an outgoing modified QsCompilation based on the incoming QsCompilation. In addition to performing compilation transformation, it also allows us to provide precondition and postcondition verification, though that is something that we will not need to do.

The full code of the rewrite step is shown below. It is entirely based on the official CsharpGeneration step – with the main difference being that it does not write out the C# code into physical files, but keeps them in memory. However, all the generated C# is identical. Notice that we don’t really modify the Q# compilation – we just use the step to extract the C# code based on it.

class InMemoryEmitter : IRewriteStep
    {
        public static Dictionary<string, string> GeneratedFiles { get; } = new Dictionary<string, string>();

        private readonly Dictionary<string, string> _assemblyConstants = new Dictionary<string, string>();
        private readonly List<IRewriteStep.Diagnostic> _diagnostics = new List<IRewriteStep.Diagnostic>();

        public string Name => "InMemoryCsharpGeneration";

        public int Priority => -2;

        public IDictionary<string, string> AssemblyConstants => _assemblyConstants;

        public IEnumerable<IRewriteStep.Diagnostic> GeneratedDiagnostics => _diagnostics;

        public bool ImplementsPreconditionVerification => false;

        public bool ImplementsTransformation => true;

        public bool ImplementsPostconditionVerification => false;

        public bool PreconditionVerification(QsCompilation compilation)
        {
            throw new NotImplementedException();
        }

        public bool Transformation(QsCompilation compilation, out QsCompilation transformed)
        {
            var context = CodegenContext.Create(compilation, _assemblyConstants);
            var sources = GetSourceFiles.Apply(compilation.Namespaces);

            foreach (var source in sources.Where(s => !s.Value.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)))
            {
                var content = SimulationCode.generate(source, context);
                GeneratedFiles.Add(source.Value, content);
            }

            if (!compilation.EntryPoints.IsEmpty)
            {
                var callable = context.allCallables.First(c => c.Key == compilation.EntryPoints.First()).Value;
                var content = EntryPoint.generate(context, callable);
                NonNullable<string> entryPointName = NonNullable<string>.New(callable.SourceFile.Value + ".EntryPoint");
                GeneratedFiles.Add(entryPointName.Value, content);
            }

            transformed = compilation;
            return true;
        }

        public bool PostconditionVerification(QsCompilation compilation)
        {
            throw new NotImplementedException();
        }
    }

Unfortunately the one thing that is not particularly elegant about the code above is that we need to capture the generated C# files into a static property. The reason for that, is that at the moment, the lifetime of the rewrite steps (or more specifically, their initialization) is managed by the compiler itself. Therefore we do not have access to the object instance representing the step after the compiler initialized and used them – which would be necessary to extract any state out of them.

To point the Q# compiler at our custom rewrite step, we should create a relevant configuration object. Since, as mentioned, the compiler controls the lifetime of the steps, we only need to tell it which assembly it should look at when discovering the steps (in our case, the current application’s assembly).

// to load our custom rewrite step, we need to point Q# compiler config at our current assembly
var config = new CompilationLoader.Configuration
{
    IsExecutable = true,
    RewriteSteps = new List<(string, string)>
    {
        ( Assembly.GetExecutingAssembly().Location, null),
    },
};

We shall also mark the Q# application as executable, meaning it can be invoked without a driver written in another language. The consequence of this is that the generated C# code will be a command line application too, with a relevant entry point.

Finally it’s time to invoke the Q# compiler – we do have a set of references, the relevant configuration, and – of course – the Q# code captured into a string variable. This is enough to get us going, but I will introduce one other component, and that is a custom logger. The compiler exposes an ILogger interface, along with an abstract LogTracker type that can be used to capture the output of the internal activities of the compiler. Remember that we already tapped into the compiler’s event stream earlier, but this is somewhat orthogonal to it as it contains much finer level of details and direct access to e.g. exception stack traces.

In our case, for example, we’d like to be sure that our compilation rewrite step has been loaded successfully from the location we are at, and this is what the logger gives us. A basic implementation of the logger is shown below:

public class ConsoleLogger : LogTracker 
{
    private readonly Func<Diagnostic, string> applyFormatting;

    protected internal virtual string Format(Diagnostic msg) =>
        this.applyFormatting(msg);

    protected sealed override void Print(Diagnostic msg) =>
        PrintToConsole(msg.Severity, this.Format(msg));

    public ConsoleLogger(
        Func<Diagnostic, string> format = null,
        DiagnosticSeverity verbosity = DiagnosticSeverity.Hint,
        IEnumerable<int> noWarn = null,
        int lineNrOffset = 0)
    : base(verbosity, noWarn, lineNrOffset) =>
        this.applyFormatting = format ?? Formatting.HumanReadableFormat;

    private static void PrintToConsole(DiagnosticSeverity severity, string message)
    {
        if (message == null)
        {
            throw new ArgumentNullException(nameof(message));
        }

        Console.WriteLine(severity.ToString() + " " + message);
    }
}

With the logger in hand we can finally initialize our compiler. This is done via the CompilationLoader type. Interestingly, the way the compiler is implemented, simply instantiating this type will already invoke the entire compilation pipeline.

// compile Q# code
var compilationLoader = new CompilationLoader(loadFromDisk =>
    new Dictionary<Uri, string> { { new Uri(Path.GetFullPath("__CODE_SNIPPET__.qs")), qsharpCode } }.ToImmutableDictionary(), 
    qsharpReferences, 
    options: config, 
    logger: new ConsoleLogger());

The Q# compiler normally expects a list of Q# source files to be passed into it, but it also supports compiling arbitrary piece of code using a custom loader, which is something we leverage here to “resolve” the file contents from a local variable. We still need to give it a dummy filename, which could be any name, but the convention already used in the command line Q# compiler, which is as good as any, is to name such files CODE_SNIPPET.qs.

At this point, the compilation will have already completed, which means we can have a look at compiler’s diagnostics. There are two things that could have happened – either the compiler emitted some error diagnostics, and the compilation was unsuccessful, or the compilation succeeded. In either case, we’d want the diagnostics to be printed out, and if errors really happened, well, we should exit the program because there is no point in continuing.

// print any diagnostics
if (compilationLoader.LoadDiagnostics.Any())
{
    Console.WriteLine("Diagnostics:" + Environment.NewLine + string.Join(Environment.NewLine, diagnostics.Select(d => $"{d.Severity} {d.Code} {d.Message}")));

    // if there are any errors, exit
    if (compilationLoader.LoadDiagnostics.Any(d => d.Severity == Microsoft.VisualStudio.LanguageServer.Protocol.DiagnosticSeverity.Error))
    {
        return;
    }
}

If there are no errors, we can proceed towards compiling the C# code – using the C# output we generated in the custom rewrite step in the Q# compiler. We will be using, naturally, the Roslyn C# compiler for that. Similarly to how we needed to bootstrap the references for Q# compiler, we need to gather the necessary references here too. They need to cover multiple things – the regular BCL C# types (e.g. System.Runtime), the contract assemblies (e.g. netstandard), the types that are used to represent Q# in C# code and simulate it (e.g. Microsoft.Quantum.Simulators or Microsoft.Quantum.Runtime.Core) and finally the support types needed by the emitted C# code (e.g. System.CommandLine). The full list of references for our case is below – depending on the type of your code it might be larger for you, especially when you use custom Q# libraries.

// necessary references to compile C# simulation of the Q# compilation
var csharpReferences = new string[]
{
    "Microsoft.Quantum.QSharp.Core",
    "Microsoft.Quantum.Runtime.Core",
    "Microsoft.Quantum.Simulators",
    "Microsoft.Quantum.EntryPointDriver",
    "System.CommandLine",
    "System.Runtime",
    "netstandard",
    "System.Collections.Immutable",
    typeof(object).Assembly.FullName,
}.Select(x => Assembly.Load(new AssemblyName(x))).Select(a => a.Location);

Since we captured all the C# generated code into a static property GeneratedFiles of our custom rewrite step in the Q# compiler, we can now use those to create Roslyn C# syntax trees that we can then feed into the C# compiler.

// we captured the emitted C# syntax trees into a static variable in the rewrite step
var syntaxTrees = InMemoryEmitter.GeneratedFiles.Select(x => CSharpSyntaxTree.ParseText(x.Value));

The next step is to actually invoke the C# compiler – using the above metadata references and the syntax trees as input. Just like in the case of Q# compilation, we need to check the diagnostics – which we will want to print out, should there be any. If there are any errors, we also need to break the entire procedure.

// print any diagnostics
var csharpDiagnostics = csharpCompilation.GetDiagnostics().Where(d => d.Severity != DiagnosticSeverity.Hidden);
if (csharpDiagnostics.Any())
{
    Console.WriteLine("C# Diagnostics:" + Environment.NewLine + string.Join(Environment.NewLine, csharpDiagnostics.Select(d => $"{d.Severity} {d.Id} {d.GetMessage()}")));

    // if there are any errors, exit
    if (csharpDiagnostics.Any(d => d.Severity == DiagnosticSeverity.Error))
    {
        return;
    }
}

The final step is to emit the C# compilation into an in memory assembly and then run it from that assembly using reflection – leveraging the emitted entry point. The entry point will have a special name QsEntryPoint as created by the C# generation code we used.

// emit C# code into an in memory assembly
using var peStream = new MemoryStream();
var emitResult = csharpCompilation.Emit(peStream);
peStream.Position = 0;
var qsharpLoadContext = new QSharpLoadContext();

// run the assembly using reflection
var qsharpAssembly = qsharpLoadContext.LoadFromStream(peStream);

// the entry point has a special name "__QsEntryPoint__"
var entryPoint = qsharpAssembly.GetTypes().First(x => x.Name == "__QsEntryPoint__").GetMethod("Main", BindingFlags.NonPublic | BindingFlags.Static);
var entryPointTask = entryPoint.Invoke(null, new object[] { null }) as Task<int>;
await entryPointTask;
qsharpLoadContext.Unload();

We ended up using a custom load context here – this is not really necessary, but is a nice pattern to keep in mind, since .NET Core 3.0+ supports collectible assemblies. The code for the load context is very simple – it simply marks itself as collectible.

public class QSharpLoadContext : AssemblyLoadContext
{
    public QSharpLoadContext() : base(isCollectible: true)
    {
    }
}

And that’s it! At this point, the entire end-to-end Q# compiler and simulation should have been programmatically invoked – without using the QDK command line machinery, without creating any temporary C# files or any temporary DLLs.

The output should look similar to this:

OverallCompilation - Start
Information 
Information: 
Loaded rewrite steps that are executing as part of the compilation process
    InMemoryCsharpGeneration (file:///Users/filip/Documents/dev/Strathweb.Samples.QSharpCompiler/bin/Debug/netcoreapp3.1/Strathweb.Samples.QSharpCompiler.dll)
OverallCompilation SourcesLoading - Start
OverallCompilation SourcesLoading - End
OverallCompilation ReferenceLoading - Start
Information 
Information: 
Compiling with referenced assemblies
    /Users/filip/Documents/dev/Strathweb.Samples.QSharpCompiler/bin/Debug/netcoreapp3.1/Microsoft.Quantum.QSharp.Core.dll
    /Users/filip/Documents/dev/Strathweb.Samples.QSharpCompiler/bin/Debug/netcoreapp3.1/Microsoft.Quantum.Runtime.Core.dll
OverallCompilation ReferenceLoading - End
OverallCompilation Build - Start
OverallCompilation Build - End
OverallCompilation RewriteSteps - Start
OverallCompilation RewriteSteps - End
OverallCompilation OutputGeneration - Start
OutputGeneration SyntaxTreeSerialization - Start
OutputGeneration SyntaxTreeSerialization - End
OverallCompilation OutputGeneration - End
 OverallCompilation - End
C# Diagnostics:
Warning CS1702 Assuming assembly reference 'System.Collections.Immutable, Version=1.2.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' used by 'Microsoft.Quantum.EntryPointDriver' matches identity 'System.Collections.Immutable, Version=1.2.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' of 'System.Collections.Immutable', you may need to supply runtime policy
Ones: 58
Zeros: 42

The individual compilation steps are logged as part of the CompilationTaskEvent infrastructure into which we tapped in the beginning. On the other hand, the details related to the actual rewrite steps and Q# assembly references used, are provided by the ILogger component. There are no Q# compiler diagnostics in this output and a single C# diagnostic. It doesn’t cause any trouble, it’s just a mismatch on the System.Collections.Immutable between the one used in the compiler and in our application; this could be aligned by changing the version used by our application. That said, I left it in just to illustrate that the diagnostics actually work.

Ultimately, at the end we see the simulated output of the Q# application – from our C# application. And this was the goal of this blog post.

Ones: 58
Zeros: 42

The full code is available on Github – hopefully you will find this useful.

Read the whole story
alvinashcraft
11 hours ago
reply
West Grove, PA
Share this story
Delete
Next Page of Stories