Como criar e gerenciar Q# projetos e bibliotecas personalizadas

Neste artigo, você aprenderá a criar, gerenciar e compartilhar Q# projetos. Um Q# projeto é uma estrutura de pastas com vários Q# arquivos que podem acessar as operações e funções uns dos outros. Os projetos ajudam você a organizar logicamente o código-fonte. Você também pode usar projetos como bibliotecas personalizadas que podem ser acessados de fontes externas.

Pré-requisitos

Para executar programas Python, você também precisa:

  • Um ambiente Python com Python e Pip instalados.

  • A biblioteca qdkPython com o azure extra.

    python -m pip install --upgrade "qdk[azure]"
    

Como Q# funcionam os projetos

Um Q# projeto contém um Q# arquivo de manifesto chamado qsharp.json, e um ou mais .qs e .qsc arquivos em uma estrutura de pasta especificada. Você pode criar um Q# projeto manualmente ou diretamente em VS Code.

Quando você abre um arquivo .qs ou .qsc em VS Code, o compilador pesquisa a hierarquia de pastas ao redor para o arquivo de manifesto e determina o escopo do projeto. Se o compilador não encontrar um arquivo de manifesto, o compilador operará em um único modo de arquivo.

Quando você define o project_root em um arquivo Jupyter Notebook ou Python, o compilador procura o arquivo de manifesto na pasta project_root.

Um projeto Q# externo é um projeto Q# padrão localizado em outro diretório ou em um repositório de GitHub público e atua como uma biblioteca personalizada. Um projeto externo usa export instruções para definir as funções e as operações acessíveis por programas externos. Os programas definem o projeto externo como uma dependência em seu arquivo de manifesto e usam import instruções para acessar os itens no projeto externo, como operações, funções, structs e namespaces. Para obter mais informações, consulte Usando projetos como dependências externas.

Definir um Q# projeto

Um Q# projeto é definido pela presença de um qsharp.json arquivo de manifesto e uma src pasta, que devem estar na pasta raiz do projeto. A src pasta contém os arquivos de origem Q# . Para Q# programas e projetos externos, o Q# compilador detecta automaticamente a pasta do projeto. Para Python programas e arquivos Jupyter Notebook, você deve especificar a pasta do projeto Q# com uma chamada qsharp.init. No entanto, a estrutura de pastas de um Q# projeto é a mesma para todos os tipos de programas.

A estrutura e a hierarquia de pastas de um Q# projeto.

Definir a pasta do projeto para Q# programas

Quando você abre um arquivo .qs em VS Code, o compilador Q# pesquisa na estrutura de pastas, subindo, por um arquivo de manifesto. Se o compilador encontrar um arquivo de manifesto, o compilador incluirá todos os Q# arquivos no /src diretório e seus subdiretórios. Os itens definidos em cada arquivo ficam disponíveis para todos os outros arquivos dentro do projeto.

Por exemplo, considere a seguinte estrutura de pastas:

  • Projeto_de_Teletransporte
    • qsharp.json
    • Src
      • Main.qs
      • TeleportOperações
        • TeleportLib.qs
        • PrepareState
          • PrepareStateLib.qs

Quando você abre o arquivo /src/TeleportOperation/PrepareState/PrepareStateLib.qs, o Q# compilador faz o seguinte:

  1. Verifica /src/TeleportOperation/PrepareState/ para qsharp.json.
  2. Verifica /src/TeleportOperation para qsharp.json.
  3. Verifica /src para qsharp.json.
  4. Verifica /Teleportation_project para qsharp.json e localiza o arquivo.
  5. Estabelece /Teleportation_project como o diretório raiz do projeto e inclui todos os arquivos .qs e .qsc no diretório /src no projeto. Se o arquivo de manifesto.

Observação

Se você incluir referências explícitas aos caminhos de arquivo de .qs e .qsc em qsharp.json, o compilador carregará esses arquivos e não realizará o processo de descoberta automática. Referências explícitas de caminho de arquivo são necessárias somente quando você define uma biblioteca para ser carregável de uma referência do Git.

Criar um arquivo de manifesto

Um arquivo de manifesto é um arquivo JSON nomeado qsharp.json que pode incluir campos opcionais author, license e lints. O arquivo de manifesto mínimo viável é a cadeia de caracteres {}. Quando você cria um projeto em Q#, um arquivo de manifesto mínimo é criado para você em VS Code.

{}

Exemplos de arquivo de manifesto

Os exemplos a seguir mostram como os arquivos de manifesto definem o escopo do projeto Q# .

  • Neste exemplo, author é o único campo especificado, portanto, todos os .qs arquivos neste diretório e seus subdiretórios são incluídos no Q# projeto.

    {
        "author":"Microsoft"
    }
    
  • Dentro de um Q# projeto, você também pode usar o arquivo manifesto para ajustar as configurações do VS CodeQ# Linter. Por padrão, as três regras do Linter são:

    • needlessParens: padrão = allow

    • divisionByZero: padrão = warn

    • redundantSemicolons: padrão = warn

      Você pode definir cada regra no arquivo de manifesto como allow, warnou error. Por exemplo:

      {
          "author":"Microsoft",
          "lints": [
              {
                "lint": "needlessParens",
                "level": "allow"
              },
              {
                "lint": "redundantSemicolons",
                "level": "warn"
              },
              {
                "lint": "divisionByZero",
                "level": "error"
              }
            ]
      }
      
  • Você também pode usar o arquivo de manifesto para definir um projeto externo Q# como uma dependência e acessar remotamente operações e funções nesse projeto externo. Para obter mais informações, consulte Usando projetos como dependências externas.

Q# Requisitos e propriedades do projeto

Os requisitos e configurações a seguir se aplicam a todos os Q# projetos.

  • Todos os .qs arquivos que você deseja incluir no projeto devem estar em uma pasta nomeada src, que deve estar na pasta raiz do Q# projeto. Quando você cria um projeto em Q#, a pasta VS Code é criada automaticamente.

  • O arquivo de manifesto deve estar no mesmo nível da src pasta. Quando você cria um projeto em VS Code, um arquivo de manifesto mínimo é criado automaticamente.

  • Use import instruções para fazer referência a operações e funções de outros arquivos no projeto.

    import MyMathLib.*;  //imports all the callables in the MyMathLib namespace
    
    ...
    
    Multiply(x,y);
    

    Ou faça referência a eles individualmente usando o namespace.

    MyMathLib.Multiply(x,y); 
    

Somente para Q# projetos

  • Você pode definir uma operação de ponto de entrada em somente um arquivo .qs em um projeto Q#, que é a operação padrão Main().
  • Você deve colocar o arquivo .qs com a definição do ponto de entrada em uma camada abaixo do diretório do projeto onde está o arquivo de manifesto.
  • Todas as operações e funções no Q# projeto que são armazenadas em cache de uma .qs exibição no texto preditivo em VS Code.
  • Se o namespace de uma operação ou função selecionada ainda não foi importado, o VS Code adicionará automaticamente a instrução necessária import.

Como criar um Q# projeto

Para criar um Q# projeto, siga estas etapas:

  1. No explorador VS Code de arquivos, vá para a pasta que você deseja usar como a pasta raiz do Q# projeto.

  2. Abra o menu Exibir e escolha Paleta de Comandos.

  3. Enter QDK: Criar Q# projeto. VS Code cria um arquivo de manifesto mínimo na pasta e adiciona uma /src pasta com um Main.qs arquivo de modelo.

  4. Edite o arquivo de manifesto do seu projeto. Consulte Exemplos de arquivos de manifesto.

  5. Adicione e organize seus Q# arquivos de origem na /src pasta.

  6. Se você estiver acessando o projeto Q# de um programa Python ou Jupyter Notebook, defina o caminho da pasta root com qsharp.init. Este exemplo pressupõe que seu /src programa esteja na pasta do Q# projeto:

    qsharp.init(project_root = '../Teleportation_project')
    
  7. Se você estiver usando apenas arquivos Q# em VS Code, o compilador procurará um arquivo de manifesto quando você abrir um arquivo Q# e determinará a pasta raiz do projeto. Em seguida, o compilador examina /src e seus subdiretórios para arquivos .qs e .qsc.

Observação

Em vez disso, você pode criar manualmente o arquivo de manifesto e a /src pasta.

Projeto de exemplo

Este programa de teletransporte quântico é um exemplo de um Q# projeto executado no simulador local em VS Code. Para executar o programa em Azure Quantum hardware ou simuladores de terceiros, consulte Comece com programas Q# e VS Code para obter as etapas necessárias para compilar seu programa e se conectar ao seu workspace Azure Quantum.

Este exemplo tem a seguinte estrutura de diretório:

  • Projeto_de_Teletransporte
    • qsharp.json
    • Src
      • Main.qs
      • TeleportOperações
        • TeleportLib.qs
        • PrepareState
          • PrepareStateLib.qs

O arquivo de manifesto contém os campos author e license :

{
    "author":"Microsoft",
    "license":"MIT"
}

Q# Arquivos de origem

O arquivo Main.qs principal contém o ponto de entrada e faz referência ao TeleportOperations.TeleportLib namespace de TeleportLib.qs.

    import TeleportOperations.TeleportLib.Teleport; // references the Teleport operation from TeleportLib.qs

    operation Main() : Unit {
        use msg = Qubit();
        use target = Qubit();

        H(msg);
        Teleport(msg, target); // calls the Teleport() operation from TeleportLib.qs
        H(target);

        if M(target) == Zero {
            Message("Teleported successfully!");
        
        Reset(msg);
        Reset(target);
        }
    }

O TeleportLib.qs arquivo define a Teleport operação e chama a PrepareBellPair operação do PrepareStateLib.qs arquivo.

    import TeleportOperations.PrepareState.PrepareStateLib.*; // references the namespace in PrepareStateLib.qs
 
    operation Teleport(msg : Qubit, target : Qubit) : Unit {
        use here = Qubit();

        PrepareBellPair(here, target); // calls the PrepareBellPair() operation from PrepareStateLib.qs
        Adjoint PrepareBellPair(msg, here);

        if M(msg) == One { Z(target); }
        if M(here) == One { X(target); }

        Reset(here);
    }

O PrepareStateLib.qs arquivo contém uma operação reutilizável padrão para criar um par Bell.

    operation PrepareBellPair(left : Qubit, right : Qubit) : Unit is Adj + Ctl {
        H(left);
        CNOT(left, right);
    }

Executar os programas

Escolha a aba para o ambiente em que você executar seu programa.

Para executar este programa, abra o Main.qs arquivo VS Code e escolha Executar.

Definir Q# projetos como dependências externas

Você pode configurar Q# projetos como uma dependência externa para outros projetos, semelhante a uma biblioteca, para disponibilizar funções e operações no projeto externo Q# para outros Q# projetos. Uma dependência externa pode residir em um compartilhamento de disco ou ser publicada em um repositório público no GitHub.

Para usar um Q# projeto como uma dependência externa, você deve:

  • Adicione o projeto externo como uma dependência no arquivo de manifesto do projeto de chamada.
  • Se o projeto externo for publicado no GitHub, adicione a propriedade files ao arquivo de manifesto do projeto externo.
  • Adicione export declarações ao projeto externo.
  • Adicione import instruções ao projeto de chamada.

Configurar os arquivos de manifesto

Projetos Q# externos podem residir em um compartilhamento de unidade de rede ou local ou você pode publicá-los em um repositório GitHub público.

O arquivo de manifesto do projeto que faz chamadas

Para adicionar uma dependência a um projeto externo em um compartilhamento de disco, defina a dependência no arquivo de manifesto do projeto que faz a chamada.

{
    "author": "Microsoft",
    "license": "MIT",
    "dependencies": {
        "MyDependency": {
            "path": "/path/to/project/folder/on/disk"
        }
    }
}

No arquivo de manifesto anterior, MyDependency há uma cadeia de caracteres definida pelo usuário que identifica o namespace quando você chama uma operação. Por exemplo, se você criar uma dependência chamada MyMathFunctions, poderá chamar uma função dessa dependência com MyMathFunctions.MyFunction().

Para adicionar uma dependência a um projeto publicado em um repositório de GitHub público, use o seguinte arquivo de manifesto de exemplo:

{
    "author": "Microsoft",
    "dependencies": {
        "MyDependency": {
            "github": {
                "owner": "GitHubUser",
                "repo": "GitHubRepoName",
                "ref": "CommitHash",
                "path": "/path/to/dependency"
            }
        }
    }
}

Observação

Para dependências GitHub, ref refere-se a um GitHub refspec. Microsoft recomenda que você sempre use um hash de confirmação para poder contar com uma versão específica de sua dependência.

O arquivo de manifesto do projeto externo

Se o projeto Q# externo for publicado em um repositório de GitHub público, você deverá adicionar a propriedade files ao arquivo de manifesto do projeto externo, incluindo todos os arquivos que o projeto usa.

{
    "author": "Microsoft",
    "license": "MIT",
    "files": [ "src/MyMathFunctions.qs", "src/Strings/MyStringFunctions.qs" ]
}

A files propriedade é opcional para um projeto externo que você importa por meio "path" de uma importação local baseada em filepath. A propriedade files é necessária apenas para projetos publicados no GitHub.

Utilize a instrução export

Para tornar funções e operações em um projeto externo acessíveis aos projetos que o chamam, use a declaração export. Você pode exportar qualquer ou todos os chamáveis do arquivo. Você não pode usar a sintaxe de curinga, portanto, você deve especificar cada callable que deseja exportar.

operation Operation_A() : Unit {
...
}
operation Operation_B() : Unit  {
...
}

// makes just Operation_A available to calling programs
export Operation_A;

// makes Operation_A and Operation_B available to calling programs 
export Operation_A, Operation_B, etc.; 

// makes Operation_A available as 'OpA'
export Operation_A as OpA;

Utilize a instrução import

Para disponibilizar itens de uma dependência externa, use import declarações do programa chamador. A import instrução usa o namespace que você define para a dependência no arquivo de manifesto.

Por exemplo, considere a dependência no seguinte arquivo de manifesto:

{
    "author": "Microsoft",
    "license": "MIT",
    "dependencies": {
        "MyMathFunctions": {
            "path": "/path/to/project/folder/on/disk"
        }
    }
}

Importe os callables com o seguinte código:

import MyMathFunctions.MyFunction;  // imports "MyFunction()" from the namespace

...

A import declaração também dá suporte à sintaxe curinga e apelidos.

// imports all items from the "MyMathFunctions" namespace
import MyMathFunctions.*; 

// imports the namespace as "Math", all items are accessible via "Math.<callable>"
import MyMathFunctions as Math;

// imports a single item, available in the local scope as "Add"
import MyMathFunctions.MyFunction as Add;

// imports can be combined on one line
import MyMathFunctions.MyFunction, MyMathFunctions.AnotherFunction as Multiply; 

Exemplo de projeto externo

Para este exemplo, use o mesmo programa de teletransporte que o exemplo anterior, mas separe o programa de chamada e os callables em projetos diferentes.

  1. Crie duas pastas em sua unidade local, por exemplo Project_A e Project_B.

  2. Crie um Q# projeto em cada pasta. Para obter detalhes, consulte as etapas em Como criar um Q# projeto.

  3. No programa de chamada Project_A, copie o seguinte código para o arquivo de manifesto, mas edite o caminho conforme necessário para Project_B:

    {
      "author": "Microsoft",
      "license": "MIT",
      "dependencies": {
        "MyTeleportLib": {
          "path": "/Project_B" 
          }
        }
      }    
    
  4. Em Project_A, copie o seguinte código em Main.qs:

    import MyTeleportLib.Teleport; // imports the Teleport operation from the MyTeleportLib namespace defined in the manifest file
    
    operation Main() : Unit {
        use msg = Qubit();
        use target = Qubit();
    
        H(msg);
        Teleport(msg, target); // calls the Teleport() operation from the MyTeleportLib namespace
        H(target);
    
        if M(target) == Zero {
            Message("Teleported successfully!");
    
        Reset(msg);
        Reset(target);
        }
    }   
    
  5. Em Project_B, copie o seguinte código em Main.qs:

        operation Teleport(msg : Qubit, target : Qubit) : Unit {
            use here = Qubit();
    
            PrepareBellPair(here, target); 
            Adjoint PrepareBellPair(msg, here);
    
            if M(msg) == One { Z(target); }
            if M(here) == One { X(target); }
    
            Reset(here);
        }
    
        operation PrepareBellPair(left : Qubit, right : Qubit) : Unit is Adj + Ctl {
            H(left);
            CNOT(left, right);
        }
    
        export Teleport;       //  makes the Teleport operation available to external programs
    

    Observação

    Você não precisa exportar a operação PrepareBellPair se o seu programa em Project_A não chamar diretamente essa operação. A operação PrepareBellPair já está acessível pela operação Teleport porque PrepareBellPair está no escopo local de Project_B.

  6. Para executar o programa, abra /Project_A/Main.qsVS Code e escolha Executar.

Projetos e namespaces implícitos

Em Q# projetos, se você não especificar um namespace em um .qs programa, o compilador usará o nome do arquivo como o namespace. Em seguida, quando você faz referência a um callable de uma dependência externa, usa a sintaxe <dependencyName>.<namespace>.<callable>. No entanto, se o arquivo for nomeado Main.qs, o compilador assumirá que o namespace e a sintaxe de chamada serão <dependencyName>.<callable>. Por exemplo, import MyTeleportLib.Teleport.

Como você pode ter vários arquivos de projeto, é necessário considerar a sintaxe correta quando fizer referência a callables. Por exemplo, considere um projeto com a seguinte estrutura de arquivos:

  • /Src
    • Main.qs
    • MathFunctions.qs

O código a seguir faz chamadas para a dependência externa:

import MyTeleportLib.MyFunction;        // "Main" namespace is implied

import MyTeleportLib.MathFunctions.MyFunction;   // "Math" namespace must be explicit 

Para obter mais informações sobre o comportamento do espaço de nome, consulte Espaços de Nome de Usuário.