Skip to main content
IBM Quantum Platform

Transpiler

La funzione generate_boxing_pass_manager() è uno strumento flessibile e pratico per creare un qiskit.transpiler.PassManager in grado di raggruppare le istruzioni del circuito in riquadri annotati. Ciò offre una semplice alternativa al raggruppamento e all'annotazione manuale dei circuiti ed è utile per automatizzare un flusso di lavoro, testare diverse strategie di raggruppamento dei circuiti e così via.

Questa guida illustra come utilizzare generate_boxing_pass_manager() e i suoi argomenti. Per mettere in evidenza gli effetti di ciascuno degli argomenti della funzione, le sezioni seguenti fanno riferimento principalmente al circuito riportato di seguito.

from qiskit.circuit import Parameter, QuantumCircuit

circuit = QuantumCircuit(4, 7)
circuit.h(1)
circuit.h(2)
circuit.cz(1, 2)
circuit.h(1)
circuit.cx(1, 0)
circuit.cx(2, 3)
circuit.measure(range(1, 4), range(3))
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
for qubit in range(4):
    circuit.rz(Parameter(f"th_{qubit}"), qubit)
    circuit.rx(Parameter(f"phi_{qubit}"), qubit)
    circuit.rz(Parameter(f"lam_{qubit}"), qubit)
circuit.measure(range(4), range(3, 7))

circuit.draw("mpl", scale=0.8)
../_images/cf27e1ef03e51b2360c90165a4efeaa793da36083bb674774ca06c8181ef63dd.png

Raggruppare le operazioni in caselle

L'argomento enable_gates può essere impostato su o True False per specificare se le porte a due qubit debbano essere raggruppate in riquadri. Allo stesso modo, enable_measures indica se le misurazioni debbano essere raggruppate o meno. Il frammento di codice seguente mostra un esempio in cui sono raggruppati sia i gate che le misurazioni.

from samplomatic.transpiler import generate_boxing_pass_manager

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/b323dde8a5412e42359aef1831af92ed3702d2251547c663208dc084a2995276.png

Come si può vedere nella figura, il gestore dei passaggi crea due tipi di riquadri: quelli che contengono un singolo strato di porte a due qubit e quelli che contengono un singolo strato di misurazioni. Questa separazione riflette le prassi standard nei protocolli di apprendimento e mitigazione del rumore, che solitamente si concentrano su livelli di operazioni omogenee. I gate a due qubit e le misurazioni vengono inseriti nella casella più a sinistra che possa contenerli, mentre ogni gate a un singolo qubit viene inserito nella stessa casella del gate a due qubit o della misurazione che precede.

Il codice seguente mostra un esempio in cui enable_gates è impostato su False. Come si può notare, i gate a due qubit non sono raggruppati in riquadri, né lo sono i gate a singolo qubit che li precedono.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=False,
    enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/3fc8e660b0c7eff1f5aa2e571d064f91b3eb2738c5990727e026a8ffe1101a1c.png

Scegli come aggiungere annotazioni ai riquadri

Tutte le porte a due qubit e le caselle di misurazione nel circuito restituito presentano annotazioni con orientamento a sinistra. In particolare, tutte le caselle che contengono porte a due qubit sono contrassegnate con un Twirl. Per i riquadri di misurazione, gli utenti possono scegliere tra Twirl, BasisTranform (con mode impostato su "measure"), oppure entrambi. Il codice seguente genera un circuito in cui tutte le caselle sono ruotate e le caselle di misura sono inoltre contrassegnate con BasisTranform.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
    measure_annotations="all",
)
transpiled_circuit = boxing_pass_manager.run(circuit)

Preparare il circuito per l'iniezione di rumore

La funzione inject_noise_targets consente di specificare quali caselle debbano ricevere un’annotazione InjectNoise . Ad esempio, il codice seguente genera un circuito in cui le caselle delle porte a due qubit presentano un’annotazione InjectNoise , mentre quelle delle misurazioni no.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
    inject_noise_targets="gates",
)
transpiled_circuit = boxing_pass_manager.run(circuit)

Se un circuito contiene due o più caselle equivalenti a livello di porte a singolo qubit, tutte vengono contrassegnate con un’annotazione InjectNoise che riporta lo stesso valore ref. Pertanto, il numero di " refs" distinti nel circuito restituito è pari al numero di caselle distinte.

Il valore di influisce inject_noise_strategy sulle annotazioni InjectNoise . Sono possibili i seguenti valori:

  • modifier_ref='': Consigliato quando non è necessario modificare le mappe di rumore prima di effettuare il campionamento da esse.
  • modifier_ref=ref: Consigliato quando tutte le mappe di rumore devono essere ridimensionate in modo uniforme con lo stesso fattore.
  • Un valore unico di modifier_ref: consigliato quando ogni mappa di rumore deve essere ridimensionata con un fattore diverso.

Il codice seguente genera un circuito in cui le caselle delle porte a due qubit presentano un'annotazione InjectNoise con valori modifier_ref univoci.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
    inject_noise_targets="gates",
    inject_noise_strategy="individual_modification",
)
transpiled_circuit = boxing_pass_manager.run(circuit)

Scegli una strategia di rotazione

Il gestore dei pass di raggruppamento inizia scegliendo quali porte di intreccio e quali istruzioni di misurazione debbano apparire nelle stesse caselle. Una volta raggiunto questo obiettivo, i box non saranno, in generale, a larghezza intera, ma includeranno probabilmente solo un sottoinsieme dei qubit attivi nel circuito o presenti nel target di backend. Nelle configurazioni tipiche del “boxing pass manager”, poiché tutti i qubit su cui agisce un’istruzione “box” vengono sottoposti a twirling (anche quelli su cui non agisce alcuna istruzione all’interno del “box”), la presenza di “box” a larghezza parziale influisce sugli effetti di ottimizzazione del rumore ottenuti tramite il twirling. I qubit inattivi non vengono sottoposti a rotazione e possono, ad esempio, accumulare errori coerenti mentre viene applicato il campo. Il offre twirling_strategy diverse opzioni per estendere i qubit di cui dispone ciascuna casella, modificando così il numero di qubit inattivi:

  • "active" (impostazione predefinita) non estende i riquadri ai qubit inattivi.
  • "active_accum" estende tutti i riquadri a quei qubit su cui è già stata eseguita un’istruzione precedente nel circuito. Ciò è utile se si desidera mantenere i qubit all'inizio del circuito nel loro stato fondamentale il più a lungo possibile, evitando di farli ruotare finché non diventano attivi.
  • "active_circuit" estende tutti i riquadri a quei qubit su cui agisce una qualsiasi istruzione del circuito. Ciò risulta utile se si dispone di un circuito strutturato con livelli di intrecciamento ripetuti e si desidera ridurre al minimo il numero di caselle uniche, poiché per ciascuna di esse è necessario apprendere il rumore.
  • "all" estende tutte le caselle a tutti i qubit in tutti i registri quantistici del circuito. Ciò risulta utile come estensione nel caso in "active_circuit" cui si desideri che tutti i qubit nella QPU siano sempre in rotazione.

Nell'esempio seguente, ciascuna delle strategie viene applicata al circuito di base, dopodiché vengono riportati i risultati.

circuit = QuantumCircuit(5, 2)
circuit.h(range(4))
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.barrier()
circuit.h(range(4))
circuit.measure([1, 2], [0, 1])

circuit.draw("mpl", scale=0.6)
../_images/cdf089891dabe1650f1303c8c747aa8cf03cdde4dfc42c772fe2264bd7a49a1b.png
import matplotlib.pyplot as plt

plt.figure(figsize=(9, 6))
for idx, strategy in enumerate(["active", "active_accum", "active_circuit", "all"]):
    ax = plt.subplot(2, 2, idx + 1)
    pm = generate_boxing_pass_manager(twirling_strategy=strategy, remove_barriers="never")
    transpiled_circuit = pm.run(circuit)
    fig = transpiled_circuit.draw("mpl", scale=0.8, ax=ax)
    ax.set_title(f"twirling_strategy='{strategy}'")

plt.tight_layout()
../_images/b810ecc4c65822cf60b3d652792a4089c5f931ad1e8320eccdd89bc50a08ba6d.png

Specificare come gestire gli ostacoli

Per impostazione predefinita, le barriere vengono rimosse dal circuito dopo che tutte le porte di entanglement e le istruzioni di misurazione sono state inserite nel riquadro come indicato, comprese le loro estensioni dovute alla selezione twirling_strategy , ma prima che le porte a singolo qubit vengano aggiunte al riquadro immediatamente alla loro destra, se possibile.

Ad esempio, il seguente circuito contiene una barriera:

circuit_with_barrier = QuantumCircuit(4)
circuit_with_barrier.h(range(4))
circuit_with_barrier.cz(0, 1)
circuit_with_barrier.barrier()
circuit_with_barrier.cz(2, 3)
circuit_with_barrier.measure_all()

circuit_with_barrier.draw("mpl", scale=0.8)
../_images/f2343807597155ab5301205803954b95d29ba0d7370440bd861439fd83d1a193.png

Il comportamento predefinito impedisce che i due gate di entanglement finiscano nella stessa casella, sebbene consenta ai gate a singolo qubit di essere assorbiti nella prima casella disponibile nonostante la barriera.

boxing_pass_manager = generate_boxing_pass_manager()
transpiled_circuit = boxing_pass_manager.run(circuit_with_barrier)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/7e0812ac6b3f2405c765338ab25c2e9179165a7257ef1d996bea99f94cb39ca6.png

Impostando remove_barriers su si mantengono 'never' le barriere, in modo da limitare le operazioni sui singoli qubit.

boxing_pass_manager = generate_boxing_pass_manager(remove_barriers="never")

transpiled_circuit = boxing_pass_manager.run(circuit_with_barrier)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/6b57e625509dd8ca55fbd96994398b03ac2fe6f76f21535db9bd042d4c68c987.png

Infine, se le barriere vengono rimosse immediatamente, gli intrecciatori non sono più costretti a finire in scatole separate.

boxing_pass_manager = generate_boxing_pass_manager(remove_barriers="immediately")

transpiled_circuit = boxing_pass_manager.run(circuit_with_barrier)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/9a44c59a05a44a3e8f0ce9b7edd3ccc2896c6a68fb00ed110a7e225f52a7ca73.png

Come funziona la stratificazione

Il gestore dei passaggi di boxe esegue due passaggi chiave in sequenza:

  1. GroupGatesIntoBoxes — Suddivide i gate intrecciati in caselle. In questa fase si tralasciano le porte a un singolo qubit; vengono raggruppate solo le porte a due qubit.
  2. AbsorbSingleQubitGates — Inserisce ogni gate a singolo qubit nella casella idonea più vicina alla sua destra.

Durante la stratificazione, il programma percorre GroupGatesIntoBoxes il circuito DAG in ordine topologico e colloca ogni gate a due qubit nel primo gruppo i cui qubit non sono ancora occupati. Tre tipi di istruzioni fungono da delimitatori che svuotano il gruppo corrente dei rispettivi qubit, costringendo le operazioni successive su tali qubit a entrare in un nuovo gruppo:

  • Ostacoli
  • Istruzioni presenti sulla confezione
  • Misurazioni

I gate a singolo qubit non sono delimitatori: non hanno alcun effetto sul raggruppamento e AbsorbSingleQubitGates la gestione di quest’ultimo viene lasciata a. Al momento non viene eseguita alcuna analisi di commutatività; il raggruppamento rispetta l'ordine topologico del circuito così com'è.

Perché remove_barriers="after_stratification" è l'impostazione predefinita consigliata

La modalità predefinita è "after_stratification" perché offre due vantaggi:

  • Le barriere limitano il modo in cui vengono raggruppati i gate di entanglement (fungono da delimitatori durante la stratificazione).
  • Le barriere vengono rimosse prima dell’esecuzione AbsorbSingleQubitGates delle operazioni, quindi i gate a singolo qubit possono comunque attraversare le posizioni in cui prima si trovavano le barriere ed essere assorbiti nella casella più vicina.

Al contrario:

  • "never" mantiene delle barriere lungo tutto il percorso, il che intrappola i gate a singolo qubit tra una barriera e una scatola, impedendone l’assorbimento e lasciando rotazioni non intrecciate nel circuito.
  • "immediately" elimina gli ostacoli prima della stratificazione, in modo che non abbiano alcun effetto sul raggruppamento.
  • "finally" è utile quando si desidera inserire delle barriere nel circuito di uscita (ad esempio, a scopo di visualizzazione), ma i gate a singolo qubit tra una barriera e una casella non vengono assorbiti.

Circuiti Box ISA con barriere

I circuiti con una struttura ripetitiva — come le simulazioni dell'Hamiltoniano di Trotter, gli approcci variazionali e le sequenze di amplificazione dell'errore — vengono in genere costruiti a livello di programmazione e poi transpilati in formato ISA. Aggiungere manualmente i box a un circuito transpilato è complicato, quindi il flusso di lavoro naturale è il seguente:

  1. Costruisci il circuito logico, inserendo delle barriere ai confini degli strati che desideri diventino i confini delle caselle.
  2. Trasformare in circuiti ISA. Il transpiler di Qiskit preserva le barriere attraverso il routing e l'ottimizzazione.
  3. Gareggia generate_boxing_pass_manager() nel circuito ISA.

È necessario ricorrere a barriere quando l'estremità di uno strato non copre tutti i qubit. L'algoritmo avido, altrimenti, inserirebbe i gate del livello successivo — su quei qubit “liberi” — nello stesso gruppo. A titolo di esempio concreto, si consideri una simulazione ZZ di Heisenberg in due fasi su un anello di 5 qubit. Ogni passo Trotter presenta tre strati di incollaggio: gli incollaggi pari (0,1), (2,3), gli incollaggi dispari (1,2), (3,4) e l’incollaggio avvolgente (4,0). Dopo lo strato di legami dispari, i qubit 0 e 4 sono entrambi liberi. In assenza di una barriera, il legame avvolgente (4,0) viene raggruppato in modo “avido” con i legami del passo successivo di Trotter che utilizzano quei qubit liberi, mescolando porte logiche di passi diversi nella stessa casella.

from qiskit.circuit import Parameter, QuantumCircuit

n = 5  # 5-qubit ring: bonds (0,1), (1,2), (2,3), (3,4), (4,0)
dt = Parameter("dt")
heisenberg = QuantumCircuit(n)

for step in range(2):
    # Layer 1: even bonds (0,1) and (2,3)
    for idx in range(0, n - 1, 2):
        heisenberg.rzz(dt, idx, idx + 1)
    heisenberg.barrier()
    # Layer 2: odd bonds (1,2) and (3,4) -- qubits 0 and 4 are now free
    for idx in range(1, n - 1, 2):
        heisenberg.rzz(dt, idx, idx + 1)
    heisenberg.barrier()
    # Layer 3: wrap bond (4,0) -- both qubits were free after layer 2
    heisenberg.rzz(dt, n - 1, 0)
    if step < 1:
        heisenberg.barrier()  # separates the two Trotter steps

heisenberg.measure_all()

heisenberg.draw("mpl", scale=0.7)
../_images/172fa55c9f02964fef32fc045a50b86971126be728602b3f1e4b2437df2580b7.png

Il codice seguente traspone il circuito in formato ISA, supponendo che il dispositivo sottostante utilizzi CX come entangler nativo. Si noti che le barriere sopravvivono alla transpilazione e rimangono visibili nel circuito di output.

from qiskit.transpiler import generate_preset_pass_manager

preset_pm = generate_preset_pass_manager(
    basis_gates=["rz", "sx", "cx"],
    coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]],
    optimization_level=1,
)
isa_circuit = preset_pm.run(heisenberg)
isa_circuit.draw("mpl", scale=0.5, fold=200)
../_images/e9a3fbd6ab1a91051c5cd0273d92ecebc6ae8ebbc370c30bc55daee1fd28bab7.png

Successivamente, applicare il gestore dei passaggi di boxe al circuito ISA. Ciascuno dei tre strati di legami diventa una casella separata in ogni fase di Trotter.

boxing_pm = generate_boxing_pass_manager()
boxed_isa = boxing_pm.run(isa_circuit)
boxed_isa.draw("mpl", scale=0.5, fold=100)
../_images/b59e3c047264b3340d9af0f1ca74ad9f7d4c1705d4a5176e441346db9b678213.png

Integrare il gestore di passaggi di Samplomatic nei gestori di passaggi predefiniti di Qiskit

Il codice riportato di seguito mostra come integrare generate_boxing_pass_manager() in un gestore di passaggi predefinito di Qiskit. Il circuito transpilato è in formato ISA e contiene caselle.

from qiskit.transpiler import generate_preset_pass_manager

preset_pass_manager = generate_preset_pass_manager(
    basis_gates=["rz", "sx", "cx"],
    coupling_map=[[0, 1], [1, 2]],
    optimization_level=0,
)
boxing_pass_manager = generate_boxing_pass_manager()

# Run the boxing pass manager after the scheduling stage
preset_pass_manager.post_scheduling = boxing_pass_manager

circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(0, 2)
circuit.measure_all()

transpiled_circuit = preset_pass_manager.run(circuit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/fe943b034588a5354a87abcf51b8d805c36f4f2b30a73a39c5886bce9e156b04.png

Questo modello si integra inoltre in modo naturale con il flusso di lavoro ISA "boxing" basato su barriere. Il gestore dei pass di boxing viene eseguito come fase successiva alla pianificazione, dopo che le barriere del circuito logico originale sono sopravvissute alla transpilazione, in modo da guidare la stratificazione come previsto.

preset_pm = generate_preset_pass_manager(
    basis_gates=["rz", "sx", "cx"],
    coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]],
    optimization_level=1,
)
preset_pm.post_scheduling = generate_boxing_pass_manager()

boxed_heisenberg = preset_pm.run(heisenberg)
boxed_heisenberg.draw("mpl", scale=0.5, fold=100)
../_images/4905bf1df63cc83fa581ed006a93beb709be504a4f59fc7f663f4efde62da175.png

Controllare e modificare le annotazioni

Dopo l'auto-boxing, potresti voler controllare le annotazioni su singole caselle o modificarle. Ad esempio, per aggiungere l'iniezione di rumore, rimuovere le annotazioni o modificare un gruppo rotante. Samplomatic mette a disposizione una serie di funzioni di utilità a samplomatic.utils questo scopo. Tutte restituiscono un nuovo circuito senza modificare quello originale.

extend_annotations() aggiunge una o più annotazioni a ogni casella del circuito.

from samplomatic import InjectNoise, Twirl
from samplomatic.utils import extend_annotations

boxed = generate_boxing_pass_manager().run(circuit_with_barrier)

# Add InjectNoise to every box
with_noise = extend_annotations(boxed, InjectNoise("my_noise_ref"))
with_noise.draw("mpl", scale=0.8)
../_images/7e0812ac6b3f2405c765338ab25c2e9179165a7257ef1d996bea99f94cb39ca6.png

filter_annotations() conserva solo le annotazioni dei tipi specificati, eliminando le altre. replace_annotations() applica una funzione richiamabile a ciascuna annotazione; la funzione richiamabile restituisce un elenco di annotazioni da inserire al suo posto (il valore "return" per [] rimuoverla, un elenco con un solo elemento per sostituirla oppure un elenco con più elementi per espanderla).

from samplomatic.utils import filter_annotations, replace_annotations

# Keep only Twirl annotations, drop everything else
twirl_only = filter_annotations(with_noise, Twirl)

# Replace the twirling group on every Twirl annotation
updated = replace_annotations(
    twirl_only, lambda a: [Twirl(group="local_c1")] if isinstance(a, Twirl) else [a]
)

La funzione map_annotations() generica accetta qualsiasi oggetto richiamabile che mappa un elenco di annotazioni su un nuovo elenco, offrendoti il pieno controllo sulle trasformazioni per ogni casella. strip_annotations() è una funzione di comodità che rimuove tutte le annotazioni da ogni riquadro.

Per modifiche in blocco, come l'aggiunta di InjectNoise a tutte le scatole di controllo, il parametro inject_noise_targets di generate_boxing_pass_manager() rappresenta l'opzione più concisa. Queste funzioni sono utili per apportare modifiche selettive a livello di singolo box che non sono coperte dai parametri del gestore dei passaggi.


Costruire un circuito

Ogni gestore di pass generato da generate_boxing_pass_manager() restituisce circuiti che possono essere trasformati con successo in una coppia template/samplex tramite build(). Ad esempio, il codice seguente chiama la funzione build() su un circuito generato da un gestore di passaggi di boxe.

from samplomatic import build

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit)

template, samplex = build(transpiled_circuit)

Per garantire che qualsiasi circuito transpilato possa essere compilato correttamente, i gestori dei passaggi sanno come includere caselle aggiuntive quando necessario. Si consideri, ad esempio, il circuito riportato di seguito, che termina con un qubit non misurato 0.

circuit_with_unmeasured_qubit = QuantumCircuit(4, 3)
circuit_with_unmeasured_qubit.cz(0, 1)
circuit_with_unmeasured_qubit.cz(2, 3)
for qubit in range(4):
    circuit_with_unmeasured_qubit.rz(Parameter(f"th_{qubit}"), qubit)
    circuit_with_unmeasured_qubit.rx(Parameter(f"phi_{qubit}"), qubit)
    circuit_with_unmeasured_qubit.rz(Parameter(f"lam_{qubit}"), qubit)
circuit_with_unmeasured_qubit.measure(range(1, 4), range(3))

circuit_with_unmeasured_qubit.draw("mpl", scale=0.8)
../_images/0452779799470d2deb4d32ce19f5aa28dfe8055a064e67c0e8e5684389845b0f.png

Se si disegnassero dei riquadri orientati verso sinistra attorno alle porte e alle misure, si otterrebbe un circuito con porte virtuali non raccolte sul qubit 0, e l’esecuzione build() di questo circuito comporterebbe un errore. Per evitare ciò, i gestori di pass restituiti da generate_boxing_pass_manager() possono aggiungere caselle allineate a destra che fungano da collettori. Ad esempio, nel codice seguente, il qubit 0 è chiuso da una casella con freccia a destra che raccoglie il gate virtuale non raccolto. Anche i gate a singolo qubit che agiscono sul qubit 0 vengono collocati all’interno della scatola, al fine di ridurre al minimo la profondità del circuito risultante.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit_with_unmeasured_qubit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/711ee3c014edf6f70ede5dfa849494286e96870b3bd70a2a89f687d83cc5bfc1.png

In un altro esempio, viene aggiunta una casella “right-dressed” per raccogliere i gate virtuali che altrimenti rimarrebbero non raccolti a causa delle misurazioni non incapsulate.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=False,
)
transpiled_circuit = boxing_pass_manager.run(circuit_with_unmeasured_qubit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/d59d0c0af3c47f474a964a455533b99e09a65364454ec04ced7581e6afac73ab.png
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.