Come creare e gestire Q# progetti e librerie personalizzate

Questo articolo illustra come creare, gestire e condividere Q# progetti. Un Q# progetto è una struttura di cartelle con più Q# file che possono accedere tra loro operazioni e funzioni. I progetti consentono di organizzare logicamente il codice sorgente. È anche possibile usare progetti come librerie personalizzate a cui è possibile accedere da origini esterne.

Prerequisiti

  • Un'area di lavoro Azure Quantum nella sottoscrizione Azure. Per creare un'area di lavoro, vedere Creare un'area di lavoro Azure Quantum.
  • () con le estensioni installate () e .
  • Per pubblicare il progetto esterno in un repository di GitHub pubblico, è necessario disporre di un account GitHub.

Per eseguire programmi Python, è necessario anche:

  • Ambiente Python con Python e Pip installato.

  • La libreria qdkPython con l'extra azure.

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

Funzionamento Q# dei progetti

Un Q# progetto contiene un Q# file manifesto denominato qsharp.json, e uno o più .qs file e .qsc file in una struttura di cartelle specificata. È possibile creare un Q# progetto manualmente o direttamente in VS Code.

Quando si apre un .qs file o .qsc in VS Code, il compilatore cerca il file manifesto nella gerarchia di cartelle circostanti e determina l'ambito del progetto. Se il compilatore non trova un file manifesto, il compilatore opera in una singola modalità file.

Quando si imposta il project_root in un file Jupyter Notebook o Python, il compilatore cerca il file manifesto nella cartella project_root.

Un progetto Q# esterno è un progetto Q# standard che si trova in un'altra directory o in un repository GitHub pubblico e funge da libreria personalizzata. Un progetto esterno usa istruzioni export per definire le funzioni e le operazioni accessibili da programmi esterni. I programmi definiscono il progetto esterno come dipendenza nel file manifest e usano import istruzioni per accedere agli elementi del progetto esterno, ad esempio operazioni, funzioni, struct e namespace. Per ulteriori informazioni, vedere l'uso di progetti come dipendenze esterne.

Definire un Q# progetto

Un Q# progetto è definito dalla presenza di un qsharp.json file manifesto e di una src cartella, entrambi presenti nella cartella radice del progetto. La src cartella contiene i Q# file di origine. Per Q# i programmi e i progetti esterni, il Q# compilatore rileva automaticamente la cartella del progetto. Per Python programmi e Jupyter Notebook file, è necessario specificare la cartella del progetto Q# con una chiamata qsharp.init. Tuttavia, la struttura di cartelle per un Q# progetto è la stessa per tutti i tipi di programmi.

Struttura di cartelle e gerarchia per un Q# progetto.

Definire la cartella del progetto per i programmi Q#

Quando si apre un .qs file in VS Code, il Q# compilatore cerca verso l'alto nella struttura di cartelle un file manifesto. Se il compilatore trova un file manifesto, il compilatore include tutti i Q# file nella /src directory e le relative sottodirectory. Gli elementi definiti in ogni file diventano disponibili per tutti gli altri file all'interno del progetto.

Si consideri ad esempio la struttura di cartelle seguente:

  • Progetto_teletrasporto
    • qsharp.json
    • src
      • Main.qs
      • TeleportOperations
        • TeleportLib.qs
        • PrepareState
          • PrepareStateLib.qs

Quando si apre il file /src/TeleportOperation/PrepareState/PrepareStateLib.qs, il Q# compilatore esegue le operazioni seguenti:

  1. Controlla /src/TeleportOperation/PrepareState/ per qsharp.json.
  2. Controlla /src/TeleportOperation per qsharp.json.
  3. Controlla /src per qsharp.json.
  4. /Teleportation_project Controlla qsharp.json e trova il file.
  5. Imposta /Teleportation_project come directory radice del progetto e include tutti i file .qs e .qsc nella directory /src del progetto. Se il file manifest.

Nota

Se si includono riferimenti espliciti a .qs e .qsc percorsi di file in qsharp.json, il compilatore carica tali file e non esegue il processo di individuazione automatica. I riferimenti espliciti al percorso del file sono necessari solo quando si definisce una libreria da caricare da un riferimento Git.

Creare un file manifesto

Un file manifesto è un file JSON denominato qsharp.json che può includere campi facoltativi author, licensee lints . Il file manifesto minimo praticabile è la stringa {}. Quando si crea un Q# progetto in VS Code, viene creato automaticamente un file manifesto minimo.

{}

Esempi di file manifesto

Gli esempi seguenti illustrano come i file manifesto definiscono l'ambito del Q# progetto.

  • In questo esempio, author è l'unico campo specificato, quindi tutti i file in questa directory e le .qs relative sottodirectory vengono inclusi nel Q# progetto.

    {
        "author":"Microsoft"
    }
    
  • All'interno di un Q# progetto, è anche possibile usare il file manifesto per ottimizzare le VS CodeQ# impostazioni Linter. Per impostazione predefinita, le tre regole Linter sono:

    • needlessParens: default = allow

    • divisionByZero: default = warn

    • redundantSemicolons: default = warn

      È possibile impostare ogni regola nel file manifesto su allow, warno error. Per esempio:

      {
          "author":"Microsoft",
          "lints": [
              {
                "lint": "needlessParens",
                "level": "allow"
              },
              {
                "lint": "redundantSemicolons",
                "level": "warn"
              },
              {
                "lint": "divisionByZero",
                "level": "error"
              }
            ]
      }
      
  • È anche possibile usare il file manifesto per definire un progetto esterno Q# come dipendenza e accedere in remoto a operazioni e funzioni in tale progetto esterno. Per ulteriori informazioni, vedere l'uso di progetti come dipendenze esterne.

Q# requisiti e proprietà del progetto

I requisiti e le configurazioni seguenti si applicano a tutti i Q# progetti.

  • Tutti i .qs file che si desidera includere nel progetto devono trovarsi in una cartella denominata src, che deve trovarsi nella cartella radice del Q# progetto. Quando si crea un Q# progetto in VS Code, la /src cartella viene creata automaticamente.

  • Il file manifesto deve essere allo stesso livello della src cartella. Quando si crea un Q# progetto in VS Code, viene creato automaticamente un file manifesto minimo.

  • Utilizzare le istruzioni import per fare riferimento alle operazioni e funzioni da altri file nel progetto.

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

    In alternativa, fai riferimento a ciascuno singolarmente utilizzando il namespace.

    MyMathLib.Multiply(x,y); 
    

Solo per progetti Q#

  • È possibile definire un'operazione di punto di ingresso in un .qs solo file di un Q# progetto, ovvero l'operazione Main() per impostazione predefinita.
  • È necessario inserire il file .qs con la definizione del punto di ingresso in una directory del progetto al di sotto del livello del file manifesto.
  • Tutte le operazioni e le funzioni nel progetto Q# che sono memorizzate nella cache da una visualizzazione .qs appaiono in un testo predittivo in VS Code.
  • Se lo spazio dei nomi per un'operazione o una funzione selezionata non è ancora importato, VS Code aggiunge automaticamente l'istruzione necessaria import .

Come creare un Q# progetto

Per creare un Q# progetto, seguire questa procedura:

  1. VS Code In Esplora File passa alla cartella che si vuole usare come cartella radice per il Q# progetto.

  2. Aprire il menu Visualizza e scegliere Riquadro comandi.

  3. Immettere QDK: Crea un Q# progetto. VS Code crea un file manifesto minimo nella cartella e aggiunge una /src cartella con un Main.qs file modello.

  4. Modificare il file manifest per il progetto. Vedere Esempi di file manifesto.

  5. Aggiungere e organizzare i Q# file di origine nella /src cartella .

  6. Se si accede al progetto Q# da un programma Python o Jupyter Notebook, impostare il percorso della cartella root con qsharp.init. Questo esempio presuppone che il programma si trova nella /src cartella del Q# progetto:

    qsharp.init(project_root = '../Teleportation_project')
    
  7. Se si usano solo Q# file in VS Code, il compilatore cerca un file manifesto quando si apre un Q# file e determina la cartella radice del progetto. Quindi, il compilatore analizza /src e le relative sottodirectory alla ricerca dei file .qs e .qsc.

Nota

È possibile creare manualmente il file manifesto e la /src cartella.

Progetto di esempio

Questo programma di teletrasporto quantistico è un esempio di progetto Q# eseguito nel simulatore locale in VS Code. Per eseguire il programma in Azure Quantum simulatori hardware o di terze parti, vedere Introduzione ai programmi Q# e VS Code per i passaggi per compilare il programma e connettersi all'area di lavoro Azure Quantum.

In questo esempio è presente la struttura di directory seguente:

  • Progetto_teletrasporto
    • qsharp.json
    • src
      • Main.qs
      • TeleportOperations
        • TeleportLib.qs
        • PrepareState
          • PrepareStateLib.qs

Il file manifesto contiene i campi autore e licenza :

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

Q# file di origine

Il file principale Main.qs contiene il punto di ingresso e fa riferimento allo spazio dei nomi TeleportOperations.TeleportLib da 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);
        }
    }

Il TeleportLib.qs file definisce l'operazione Teleport e chiama l'operazione PrepareBellPair dal PrepareStateLib.qs file.

    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);
    }

Il PrepareStateLib.qs file contiene un'operazione riutilizzabile standard per creare una coppia Bell.

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

Eseguire i programmi

Scegliere la scheda per l'ambiente in cui si esegue il programma.

Per eseguire questo programma, aprire il Main.qs file in VS Code e scegliere Esegui.

Configurare Q# i progetti come dipendenze esterne

È possibile configurare Q# i progetti come dipendenza esterna per altri progetti, in modo analogo a una libreria, per rendere disponibili funzioni e operazioni nel progetto esterno Q# ad altri Q# progetti. Una dipendenza esterna può risiedere su un'unità condivisa o essere pubblicata su un repository GitHub pubblico.

Per usare un Q# progetto come dipendenza esterna, è necessario:

  • Aggiungere il progetto esterno come dipendenza nel file manifesto del progetto chiamante.
  • Se il progetto esterno viene pubblicato in GitHub, aggiungere la proprietà files al file manifesto del progetto esterno.
  • Aggiungere export istruzioni al progetto esterno.
  • Aggiungere istruzioni import al progetto chiamante.

Configurare i file manifesto

I progetti Q# esterni possono risiedere in una condivisione di unità di rete o locale oppure pubblicarli in un repository GitHub pubblico.

File di manifest del progetto chiamante

Per aggiungere una dipendenza a un progetto esterno in una condivisione di unità, definire la dipendenza nel file manifesto del progetto chiamante.

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

Nel file manifesto precedente, MyDependency si tratta di una stringa definita dall'utente che identifica lo spazio dei nomi quando si chiama un'operazione. Ad esempio, se si crea una dipendenza denominata MyMathFunctions, è possibile chiamare una funzione da tale dipendenza con MyMathFunctions.MyFunction().

Per aggiungere una dipendenza a un progetto pubblicato in un repository di GitHub pubblico, usare il file manifesto di esempio seguente:

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

Nota

Per le dipendenze GitHub, ref fa riferimento a un GitHub refspec. Microsoft consiglia di usare sempre un hash di commit in modo che sia possibile basarsi su una versione specifica della dipendenza.

File manifesto del progetto esterno

Se il progetto esterno Q# viene pubblicato in un repository di GitHub pubblico, è necessario aggiungere la proprietà files al file manifesto del progetto esterno, inclusi tutti i file usati dal progetto.

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

La proprietà files è facoltativa per un progetto esterno che importi tramite "path" con un'importazione basata su un percorso file locale. La proprietà files è necessaria solo per i progetti pubblicati in GitHub.

Usare l'istruzione export

Per rendere accessibili le funzioni e le operazioni in un progetto esterno per chiamare i progetti, usare l'istruzione export . È possibile esportare qualsiasi o tutti i callable nel file. Non è possibile usare la sintassi con wildcard, pertanto è necessario specificare ogni funzione da esportare.

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;

Usare l'istruzione import

Per rendere disponibili gli elementi da una dipendenza esterna, usare le istruzioni import dal programma chiamante. L'istruzione import usa lo spazio dei nomi definito per la dipendenza nel file manifesto.

Si consideri ad esempio la dipendenza nel file manifesto seguente:

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

Importare le funzioni richiamabili con il codice seguente:

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

...

L'istruzione import supporta anche la sintassi wild card e gli alias.

// 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; 

Esempio di progetto esterno

Per questo esempio, utilizzare lo stesso programma di teletrasporto dell'esempio precedente, ma separare il programma chiamante e le funzioni chiamabili in progetti diversi.

  1. Creare due cartelle nell'unità locale, ad esempio Project_A e Project_B.

  2. Creare un Q# progetto in ogni cartella. Per informazioni dettagliate, vedere la procedura descritta in Come creare un Q# progetto.

  3. In Project_A, il programma chiamante, copia il codice seguente nel file di manifest, ma modifica il percorso secondo le necessità per Project_B:

    {
      "author": "Microsoft",
      "license": "MIT",
      "dependencies": {
        "MyTeleportLib": {
          "path": "/Project_B" 
          }
        }
      }    
    
  4. In Project_Acopiare il codice seguente in 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. In Project_Bcopiare il codice seguente in 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
    

    Nota

    Non è necessario esportare l'operazione PrepareBellPair se il programma in Project_A non chiama direttamente quell'operazione. L'operazione PrepareBellPair è già accessibile dall'operazione Teleport perché PrepareBellPair si trova nell'ambito locale di Project_B.

  6. Per eseguire il programma, aprire /Project_A/Main.qs in VS Code e scegliere Esegui.

Progetti e namespace impliciti

Nei Q# progetti, se non si specifica un namespace in un programma .qs, il compilatore usa il nome del file come namespace. Quindi, quando si fa riferimento a un oggetto chiamabile da una dipendenza esterna, si usa la sintassi <dependencyName>.<namespace>.<callable>. Tuttavia, se il file è denominato Main.qs, il compilatore presuppone che lo spazio dei nomi e la sintassi chiamante sia <dependencyName>.<callable>. Ad esempio: import MyTeleportLib.Teleport.

Poiché potrebbero essere presenti più file di progetto, è necessario tenere conto della sintassi corretta quando si fa riferimento a oggetti chiamabili. Si consideri, ad esempio, un progetto con la struttura di file seguente:

  • /Src
    • Main.qs
    • MathFunctions.qs

Il codice seguente effettua chiamate alla dipendenza esterna:

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

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

Per ulteriori informazioni sul comportamento dei namespace, vedere Namespace utente.