Skip to main content
IBM Quantum Platform

Notas de versão do Qiskit 0.34


0.34.2

Terra 0.19.2

Prelúdio

O Qiskit Terra 0.19.2 é predominantemente uma versão de correção de bugs, mas agora também vem com rodas criadas para Python 3.10 em todas as principais plataformas.

Novos Recurso

  • Foi adicionado suporte para execução com Python 3.10. Isso inclui a publicação de binários pré-compilados em PyPI para Python 3.10 em plataformas compatíveis.

Notas da Atualização

Correções de bugs

  • Foi corrigido um erro em que o CVaRMeasurement tentava converter uma PauliSumOp em uma matriz densa para verificar se ela era diagonal. Para operadores grandes (> 16 qubits), esse cálculo era extremamente caro e gerava um erro se não fosse explicitamente ativado usando qiskit.utils.algorithm_globals.massive = True. A verificação agora é eficiente mesmo para grandes números de qubits.

  • DAGCircuit.draw() e a função associada dag_drawer() agora mostrarão uma mensagem de erro mais útil quando o nome de arquivo fornecido não for válido.

  • QuantumCircuit.add_register() não causará mais referências duplicadas AncillaQubit referências duplicadas em um circuito quando receber um AncillaRegister cujos bits já estejam presentes.

  • Conversão fixa de QuantumCircuits com condições clássicas em Clbit s para Instructions ao usar a função circuit_to_instruction() ou o método QuantumCircuit.to_instruction() método. Por exemplo, o seguinte funcionará agora:

    from qiskit.circuit import QuantumCircuit, Qubit, Clbit
    
    qc = QuantumCircuit([Qubit(), Clbit()])
    qc.h(0).c_if(qc.clbits[0], 0)
    qc.to_instruction()
  • Os registros agora rejeitarão corretamente os bits duplicados. Corrigido #7446.

  • O backend simulado FakeOpenPulse2Q agora tem T2 tempos e erros de leitura armazenados para seus qubits. Esses são valores arbitrários, aproximadamente consistentes com backends reais no momento de sua criação.

  • Corrige a ordem dos qubits das evoluções de 2 qubits no PauliEvolutionGatese usado com uma síntese de fórmula de produto. Por exemplo, antes, a evolução do IIZ + IZI + IZZ

    from qiskit.circuit.library import PauliEvolutionGate
    from qiskit.opflow import I, Z
    operator = (I ^ I ^ Z) + (I ^ Z ^ I) + (I ^ Z ^ Z)
    print(PauliEvolutionGate(operator).definition.decompose())

    produzido

         ┌───────┐
    q_0:Rz(2) ├────────
         ├───────┤
    q_1:Rz(2) ├─■──────
         └───────┘ │ZZ(2)
    q_2: ──────────■──────

    enquanto que agora ele produz corretamente

         ┌───────┐
    q_0:Rz(2) ├─■──────
         ├───────┤ │ZZ(2)
    q_1:Rz(2) ├─■──────
         └───────┘
    q_2: ─────────────────
  • Foi corrigido um problema nas gavetas de circuito latex e mpl quando os nomes de registros com vários sublinhados no nome não eram exibidos corretamente.

  • Os números negativos nas saídas de matriz das gavetas agora aparecerão como números decimais em vez de frações com numeradores e denominadores enormes. Como os números positivos, eles ainda serão frações se a proporção for entre números pequenos.

  • Foi corrigido um problema com o método Target.get_non_global_operation_names() ao ser executado em um alvo, incorretamente levantava uma exceção em alvos com operações globais ideais. Anteriormente, se esse método fosse chamado em um destino que contivesse qualquer operação ideal definida globalmente, em que as propriedades da instrução estivessem definidas como None, esse método geraria uma exceção em vez de tratar essa instrução como global.

  • Foi corrigido um problema com a função transpile() em que ela podia falhar ao receber um objeto Target diretamente com o kwarg target .

  • Foi corrigido um problema com a função transpile() em que ela podia falhar quando o argumento backend era um BackendV2 ou um Target por meio do kwarg target que continha operações ideais definidas globalmente.

  • Foi corrigido um problema em que a plotagem de esferas de Bloch podia causar o surgimento de um AttributeError no Jupyter ou ao tentar cortar as figuras em tamanho reduzido com Matplotlib 3.3 ou 3.4 (mas não 3.5 ). Por exemplo, o código a seguir apresentaria um erro fatal anteriormente com uma mensagem:

    AttributeError: 'Arrow3D' object has no attribute '_path2d'

    mas agora será bem-sucedido com todas as versões atuais compatíveis do Matplotlib :

    from qiskit.visualization import plot_bloch_vector
    plot_bloch_vector([0, 1, 0]).savefig("tmp.png", bbox_inches='tight')
  • Foi corrigido um erro no PauliSumOp.permute() em que o objeto no qual o método é chamado era permutado no local, em vez de retornar uma cópia permutada. Esse bug só ocorria em permutações que deixavam inalterado o número de qubits no operador.

  • Corrigido o método PauliEvolutionGate.inverse() , que anteriormente calculava o inverso invertendo o tempo de evolução. Isso só seria o inverso correto se o operador tivesse evoluído exatamente. Em particular, isso fez com que o inverso das evoluções de tempo baseadas em Trotterização fosse incorreto.

  • O método QuantumInstance.execute() não mais modificará sua entrada se receber uma lista de circuitos.

  • Correção da serialização QPY de instruções personalizadas que tinham uma definição explícita de no-op. Anteriormente, eles eram gravados e posteriormente lidos da mesma forma como se fossem portas opacas (sem nenhuma definição). Agora, eles farão corretamente a viagem de ida e volta de uma definição vazia. Por exemplo, o seguinte agora estará correto:

    import io
    from qiskit.circuit import Instruction, QuantumCircuit, qpy_serialization
    
    # This instruction is explicitly defined as a one-qubit gate with no
    # operations.
    empty = QuantumCircuit(1, name="empty").to_instruction()
    # This instruction will perform some operations that are only known
    # by the hardware backend.
    opaque = Instruction("opaque", 1, 0, [])
    
    circuit = QuantumCircuit(2)
    circuit.append(empty, [0], [])
    circuit.append(opaque, [1], [])
    
    qpy_file = io.BytesIO()
    qpy_serialization.dump(circuit, qpy_file)
    qpy_file.seek(0)
    new_circuit = qpy_serialization.load(qpy_file)[0]
    
    # Previously both instructions in `new_circuit` would now be opaque, but
    # there is now a correct distinction.
    circuit == new_circuit
  • Adicionado um atributo BackendV2.provider para implementações da classe BackendV2 classe abstrata. Anteriormente, BackendV2 podiam ser inicializados com um provedor, mas isso não era acessível aos usuários.

  • Corrigido o suporte à classe QuantumInstance quando executado com um backend BackendV2 backend. Anteriormente, a tentativa de usar um QuantumInstance com um BackendV2 resultaria em um erro.

  • Corrigido um erro no VQE em que os parâmetros do ansatz ainda eram explicitamente classificados em ASCII por seu nome se o ansatz fosse redimensionado. Isso levou a uma ordem incompatível dos valores otimizados no atributo optimal_point do objeto de resultado.

    Em particular, esse erro ocorria se nenhum ansatz fosse definido pelo usuário e o VQE escolhesse um padrão com 11 ou mais parâmetros livres.

  • Parou o analisador em QuantumCircuit.from_qasm_str() e from_qasm_file() de aceitar programas OpenQASM que se identificavam como sendo de uma versão de linguagem diferente de 2.0. Esse analisador é apenas para OpenQASM 2.0; o suporte para circuitos importados de OpenQASM 3.0 será adicionado em uma versão futura.

  • Correção da serialização QPY de objetos QuantumCircuit objetos que continham instruções de fluxo de controle. Anteriormente, se você tentasse serializar um circuito contendo IfElseOp, WhileLoopOp, ou ForLoopOp a serialização falhava. Corrigido #7583.

  • Corrigida a serialização QPY de QuantumCircuit contendo subconjuntos de bits de um QuantumRegister ou ClassicalRegister. Anteriormente, se você tentasse serializar um circuito como esse, ele trataria incorretamente esses bits como autônomos Qubit ou Clbit sem ter um registro definido. Por exemplo, se você tentar serializar um circuito como:

    import io
    from qiskit import QuantumCircuit, QuantumRegister
    from qiskit.circuit.qpy_serialization import load, dump
    
    qr = QuantumRegister(2)
    qc = QuantumCircuit([qr[0]])
    qc.x(0)
    with open('file.qpy', 'wb') as fd:
        dump(qc, fd)

    quando esse circuito for carregado, os registros serão totalmente preenchidos corretamente, mesmo que o circuito contenha apenas um subconjunto dos bits do registro.

  • QFT agora avisará se for instanciado ou construído com configurações que farão com que ele perca a precisão, em vez de gerar um OverflowError. Isso pode acontecer se o número de qubits for muito grande (um pouco mais de 1.000) sem que o grau de aproximação seja igualmente grande. O circuito agora será construído com sucesso, mas alguns ângulos podem ser indistinguíveis de zero, devido a limitações nos números de ponto flutuante de precisão dupla.

Aer 0.10.3

Prelúdio

O Qiskit Aer 0.10.3 é principalmente uma versão de correção de bugs, corrigindo vários bugs que foram descobertos desde a versão 0.10.2. No entanto, essa versão também apresenta suporte para execução com Python 3.10, incluindo rodas binárias pré-compiladas em todas as principais plataformas. Essa versão também inclui rodas binárias pré-compiladas para arm64 em macOS.

Novos Recurso

  • Foi adicionado suporte para execução com Python 3.10. Isso inclui a publicação de binários pré-compilados em PyPI para Python 3.10 em plataformas compatíveis.

  • Foi adicionado suporte para os sistemas M1 macOS. Os binários pré-compilados para as versões compatíveis de Python >= 3.8 em arm64 macOS agora serão publicados em PyPI para esta e futuras versões.

Notas da Atualização

Correções de bugs

  • Corrige um bug no site RelaxationNoisePass em que as durações das instruções eram sempre consideradas em unidades de tempo dt, independentemente da unidade real da construção. Agora, a conversão de unidades é tratada corretamente para todas as unidades de duração de instrução.

    Consulte o número 1453 para obter detalhes.

  • Corrige um problema com LocalNoisePass para funções de ruído que retornam um QuantumCircuit para a operação de ruído. Eles foram anexados ao DAG como uma instrução de circuito opaca que deve ser desenrolada para ser simulada. Essa correção os compõe de modo que as instruções de circuito sejam adicionadas ao novo DAG e possam ser simuladas sem desenrolamento adicional se todas as instruções de circuito forem compatíveis com o simulador.

    Consulte o número 1447 para obter detalhes.

  • As transpilações multithread para gerar portas diagonais agora funcionarão corretamente se o número de portas de um circuito exceder fusion_parallelization_threshold. Anteriormente, diferentes threads ocasionalmente fundiam o mesmo elemento em vários blocos, causando resultados incorretos.

  • Corrige um bug com truncamento de circuitos em Qobjs parametrizados. Anteriormente, os parâmetros do QObj parametrizado podiam ser resolvidos incorretamente se os qubits não utilizados de seus circuitos fossem truncados, porque os índices dos parâmetros não eram atualizados depois que as instruções sobre os qubits não medidos eram removidas.

    Consulte o número 1427 para obter detalhes.

Ignis 0.7.0

Nenhuma mudança

IBM 0.18.3

Nenhuma mudança


0.34.1

Terra 0.19.1

Nenhuma mudança

Aer 0.10.2

Correções de bugs

  • Corrigida a simulação de loops for em que o parâmetro de loop não era usado no corpo do loop. Por exemplo, anteriormente, esse código falharia, mas agora será bem-sucedido:

    import qiskit
    from qiskit.providers.aer import AerSimulator
    
    qc = qiskit.QuantumCircuit(2)
    with qc.for_loop(range(4)) as i:
        qc.h(0)
        qc.cx(0, 1)
    
    AerSimulator(method="statevector").run(qc)
  • Corrige um bug no site QuantumError.to_dict() em que as instruções do circuito N-qubit em que a instrução montada sempre se aplicava aos qubits [0, ..., N-1] em vez dos qubits da instrução. Esse bug também afetou os modelos de ruído do dispositivo e do backend falso.

    Consulte a edição 1415 para obter detalhes.

Ignis 0.7.0

Nenhuma mudança

IBM 0.18.3

Nenhuma mudança


0.34.0

O Qiskit 0.34.0 inclui uma versão pontual do Qiskit Aer: versão 0.10.1, que corrige regressões de desempenho na versão 0.10.0 que foram descobertas imediatamente após o lançamento. Veja abaixo as notas de versão do Qiskit Aer 0.10.0 e do 0.10.1.

Terra 0.19.1

Nenhuma mudança

Aer 0.10.1

Prelúdio

O patch do Qiskit Aer 0.10.1 corrige as regressões de desempenho introduzidas no Qiskit Aer 0.10.0.

Correções de bugs

  • Corrigir a regressão de desempenho em simulações com ruído devido ao grande aumento na sobrecarga de serialização para carregar modelos de ruído de Python para C++, resultante de chamadas de multiprocessamento Python aninhadas não intencionais. Consulte a edição 1407 para obter detalhes.

Aer 0.10.0

Prelúdio

A versão do Qiskit Aer 0.10 inclui vários aprimoramentos no desempenho e no modelo de ruído. Alguns destaques são:

  • Desempenho aprimorado para simulações paralelas de GPU e HPC
  • Suporte para simulação de circuitos contendo instruções de fluxo de controle QASM 3.0
  • Suporte para ruído de relaxamento em circuitos programados em modelos de ruído de back-end
  • Suporte a passagens de transpilador criadas pelo usuário para definir erros de porta personalizados e modelos de ruído, e inseri-los em circuitos.

Novos Recurso

  • Foi adicionado suporte às instruções de fluxo de controle do QASM 3.0 introduzidas no Qiskit-Terra 0.19.0. As instruções suportadas são ForLoopOp, WhileLoopOp, ContinueLoopOp, BreakLoopOp, IfElseOp.

  • Adição de uma otimização de simulação de disparo em lote para simulações de GPU. Esse recurso opcional usará a memória disponível em uma ou mais GPUs para executar vários disparos de simulação em paralelo, melhorando consideravelmente o desempenho em simulações de vários disparos com modelos de ruído e/ou medições intermediárias.

    Essa opção é ativada por padrão ao usar device="GPU" e uma simulação method de "statevector" ou "density_matrix" com o AerSimulator. Ele pode ser desativado com a configuração batched_shots_gpu=False nas opções do simulador.

    Essa otimização é mais vantajosa para números pequenos e médios de qubits em que há memória suficiente na GPU para executar várias simulações em paralelo. O número máximo de qubits ativos do circuito para ativar essa otimização pode ser configurado usando a opção do simulador batch_shots_gpu_max_qubits . O valor padrão dessa opção é 16.

  • Adicionada a nova opção max_shot_size a um executor personalizado para executar várias fotos de um circuito ruidoso em paralelo.

    Por exemplo, configurar o site max_shot_size com um executor personalizado:

    backend = AerSimulator(
       max_shot_size=1, max_job_size=1, executor=custom_executor)
    job = backend.run(circuits)

    dividirá as fotos de um circuito ruidoso em vários circuitos. Após a conclusão da execução de todas as fotos individuais, os resultados do trabalho são automaticamente combinados em um único objeto Result que é retornado por job.result().

  • Adicionada a opção do simulador mps_swap_direction que permite ao usuário determinar a direção das trocas internas, quando elas são inseridas em uma porta de 2 qubit. Os valores possíveis são "mps_swap_right" e "mps_swap_left". A direção das trocas pode afetar o desempenho, dependendo do circuito.

  • Implementou uma nova otimização de amostragem de medição para o método de simulação "matrix_product_state" do AerSimulator. Atualmente, esse algoritmo é usado somente quando todos os qubits são medidos e quando a opção de simulador mps_sample_measure_algorithm está definida como "mps_probabilities".

  • Melhorou o desempenho da instrução de medida para o método de simulação "matrix_product_state" do AerSimulator.

  • Foi adicionada uma instrução SaveClifford para salvar o estado do método de simulação do estabilizador como um objeto Clifford objeto.

    Observe que essa instrução é essencialmente equivalente à instrução SaveStabilizer , mas essa instrução retornará o estado salvo como um objeto StabilizerState em vez de um objeto Clifford objeto.

  • Foram adicionadas duas passagens de transpilador para inserir erros quânticos dependentes de instruções em circuitos:

    • qiskit.providers.aer.noise.LocalNoisePass
    • qiskit.providers.aer.noise.RelaxationNoisePass

    A passagem LocalNoisePass pode ser usada para implementar modelos de ruído parametrizados personalizados, definindo uma função de geração de ruído da forma

    def fn(
        inst: Instruction,
        qubits: Optional[List[int]] = None,
    ) -> InstructionLike

    que retorna uma instrução de ruído (por exemplo, uma QuantumError ou outra instrução) que pode depender de quaisquer propriedades ou parâmetros da instrução e dos argumentos do qubit.

    Essa função pode ser aplicada a todas as instruções em um circuito ou a um subconjunto específico (consulte a documentação do site LocalNoisePass para obter mais detalhes)

    O RelaxationNoisePass é um caso especial do LocalNoisePass que usa uma função de ruído predefinida que retorna um produto tensorial de thermal_relaxation_error() em cada qubit em uma instrução, dependendo da duração da instrução e dos parâmetros de constante de tempo de relaxamento fornecidos da passagem.

  • O modelo básico de ruído de dispositivo implementado por NoiseModel.from_backend() e AerSimulator.from_backend() foi atualizado para permitir a adição de erros de relaxamento dependentes da duração em portas de atraso de circuito usando o RelaxationNoisePass.

    Para ativar esse ruído ao executar simulações ruidosas, você deve primeiro programar seu circuito para inserir instruções de atraso programadas da seguinte forma:

    backend = AerSimulator.from_backend(ibmq_backend)
    scheduled_circuit = qiskit.transpile(
        circuit, backend=backend, scheduling_method='asap')
    result = backend.run(scheduled_circuit).result()

    Se o circuito for transpilado sem ser programado (e também não contiver instruções de atraso), a simulação com ruído não incluirá o efeito dos erros de relaxamento de atraso. Nesse caso, a simulação será equivalente à simulação anterior do qiskit-aer 0.9, em que o ruído de relaxamento só é adicionado às instruções de porta com base em sua duração, conforme obtido das propriedades de back-end.

  • O construtor de QuantumError agora aceita vários novos tipos de entrada como argumento de noise_ops , por exemplo:

    import numpy as np
    
    from qiskit import QuantumCircuit
    from qiskit.circuit.library import IGate, XGate, Reset
    from qiskit.quantum_info import Kraus
    from qiskit.providers.aer.noise import QuantumError
    
    # Quantum channels
    kraus = Kraus([
        np.array([[1, 0], [0, np.sqrt(1 - 0.9)]], dtype=complex),
        np.array([[0, 0], [0, np.sqrt(0.9)]], dtype=complex)
    ])
    print(QuantumError(kraus))
    
    # Construction from a QuantumCircuit
    qc = QuantumCircuit(2)
    qc.h(0)
    qc.cx(0, 1)
    error = QuantumError(qc)
    
    # Construction from a tuple of (Instruction, List[int]), where the list of
    # integers represents the qubits.
    error = QuantumError((Reset(), [0]))
    
    # Construction from an iterable of objects in the same form as above, but
    # where each also has an associated probability.
    error = QuantumError([
        ((IGate(), [0]), 0.9),
        ((XGate(), [0]), 0.1),
    ])
    
    # A short-hand for the iterable form above, where the qubits are implicit,
    # and each instruction is over all qubits.
    error = QuantumError([(IGate(), 0.9), (XGate(), 0.1)])

    Observe que o formato de entrada original baseado em JSON está obsoleto.

  • Adicionada uma função de utilidade qiskit.providers.aer.utils.transform_noise_model() para construir um modelo de ruído aplicando uma função fornecida a todos os QuantumErrors no modelo de ruído.

  • Foram adicionadas duas funções utilitárias qiskit.providers.aer.utils.transpile_quantum_error() e qiskit.providers.aer.utils.transpile_noise_model() para transpilar os circuitos contidos em QuantumError e todos os erros em um NoiseModel.

  • Foi adicionada a capacidade de adicionar objetos QuantumError diretamente a um QuantumCircuit sem converter para uma instrução Kraus instrução.

    Os circuitos que contêm erros quânticos agora podem ser executados nos simuladores AerSimulator e QasmSimulator como uma alternativa ou além da construção de um NoiseModel para definir instruções de circuito com ruído.

    Exemplo:

    from qiskit import QuantumCircuit
    from qiskit.providers.aer import AerSimulator
    from qiskit.providers.aer.noise import pauli_error
    
    error_h = pauli_error([('I', 0.95), ('X', 0.05)])
    error_cx = pauli_error([('II', 0.9), ('XX', 0.1)])
    
    qc = QuantumCircuit(3)
    qc.h(0)
    qc.append(error_h, [0])
    qc.cx(0, 1)
    qc.append(error_cx, [0, 1])
    qc.cx(0, 2)
    qc.append(error_cx, [0, 2])
    qc.measure_all()
    
    backend = AerSimulator(method='stabilizer')
    result = backend.run(qc).result()
    result.get_counts(0)

    Os circuitos que contêm erros quânticos também podem ser avaliados usando o quantum_info canal quântico e DensityMatrix classes.

Notas da Atualização

  • O tipo de retorno de várias instruções de salvamento foi alterado para ser as classes Qiskit Terra correspondentes, em vez de matrizes ou dicionários NumPy brutos. Os tipos que foram alterados são

  • Alterado o valor padrão de standard_gates para None para todas as funções em qiskit.providers.aer.noise.errors.standard_errors , pois essas funções são atualizadas para que usem portas padrão por padrão.

  • Quando um argumento não suportado é fornecido a approximate_quantum_error(), ele agora gerará um NoiseError em vez de um RuntimeError.

Notas de descontinuação

  • O uso de métodos e atributos NumPy ndarray no tipo de retorno de save_statevector(), save_density_matrix(), save_unitary() e save_superop() foi descontinuado e deixará de funcionar em uma versão futura. Essas instruções agora retornam qiskit.quantum_info para seus tipos de retorno. A compatibilidade parcial com versões anteriores ao tratamento desses objetos como NumPy arrays é implementada pelo encaminhamento de métodos para o array interno durante o período de depreciação.

  • A passagem de um objeto BackendProperties para o argumento backend de NoiseModel.from_backend() foi descontinuado, pois é incompatível com ruídos de atraso dependentes da duração e será removido em uma versão futura. Em vez disso, passe um objeto Qiskit Terra BackendV1 em vez disso.

  • A opção number_of_qubits do construtor QuantumError foi descontinuada em favor da determinação automática da dimensão.

  • A opção standard_gates do construtor QuantumError foi descontinuada em favor da externalização dessa funcionalidade de mudança de base. Em muitos casos, você pode transformar qualquer erro em um erro definido somente com portas específicas usando approximate_quantum_error().

  • Preterida a opção standard_gates de todas as funções em qiskit.providers.aer.noise.errors.standard_errors em favor do retorno de erros na forma de uma mistura de portas padrão, tanto quanto possível, por padrão.

  • Preteridas todas as funções em errorutils porque são funções auxiliares destinadas a serem usadas somente para implementar funções em qiskit.providers.aer.noise.errors.standard_errors e deveriam ter sido fornecidas como funções privadas.

  • Preterida a opção standard_gates de NoiseModel.from_backend() em favor da externalização dessa funcionalidade de mudança de base.

  • Preterido NoiseModel.from_dict() para tornar o modelo de ruído independente do formato Qobj (JSON).

  • Preteridas todas as variáveis, funções e classes públicas em qiskit.providers.aer.noise.utils.noise_transformation , exceto approximate_quantum_error() e approximate_noise_model(), porque são funções auxiliares destinadas a serem usadas somente para implementar as funções de approximate_* e deveriam ter sido fornecidas como funções privadas.

  • Preterido remap_noise_model() , pois o código C++ agora trunca e remapeia automaticamente os modelos de ruído se truncar os circuitos.

Outras observações

  • Mudanças na implementação da função approximate_quantum_error() podem alterar o erro aproximado resultante em comparação com o Qiskit Aer 0.9.

Ignis 0.7.0

Nenhuma mudança

IBM 0.18.3

Correções de bugs

  • Correção fornecida em #1100 para um problema com a codificação e decodificação de JSON ao usar ParameterExpressions em conjunto com o Qiskit Terra 0.19.1 e superior. Anteriormente, as instâncias de Parameter reconstruídas a partir da saída JSON teriam identificadores exclusivos diferentes, fazendo com que parecessem desiguais à entrada. Agora eles terão as identidades de apoio corretas.
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.