Skip to content

Commit

Permalink
Remove psutil as a dependency (Qiskit#11336)
Browse files Browse the repository at this point in the history
* initial commit

* fixed missing import

* set sabreswap test trials to 2

* added check to determin number of cpus

* formatting error

* unittest excetion for hardware detection

* formatting error

* Fix handling of non-linux default CPU usage

* Update release note

* Expand testing

* Remove unused import

* Update mock to work on non-linux platforms

* Fix lint

---------

Co-authored-by: Matthew Treinish <[email protected]>
  • Loading branch information
FabianBrings and mtreinish authored Jan 23, 2024
1 parent fdc18d4 commit b5aaffb
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 30 deletions.
6 changes: 1 addition & 5 deletions qiskit/providers/basicaer/qasm_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@
import logging
import warnings

from math import log2
from collections import Counter
import numpy as np

from qiskit.utils.multiprocessing import local_hardware_info
from qiskit.providers.models import QasmBackendConfiguration
from qiskit.result import Result
from qiskit.providers.backend import BackendV1
Expand All @@ -53,12 +51,10 @@
class QasmSimulatorPy(BackendV1):
"""Python implementation of an OpenQASM 2 simulator."""

MAX_QUBITS_MEMORY = int(log2(local_hardware_info()["memory"] * (1024**3) / 16))

DEFAULT_CONFIGURATION = {
"backend_name": "qasm_simulator",
"backend_version": "2.1.0",
"n_qubits": min(24, MAX_QUBITS_MEMORY),
"n_qubits": 24,
"url": "https://github.com/Qiskit/qiskit-terra",
"simulator": True,
"local": True,
Expand Down
6 changes: 1 addition & 5 deletions qiskit/providers/basicaer/statevector_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
"""

import logging
from math import log2
from qiskit.utils.multiprocessing import local_hardware_info
from qiskit.providers.basicaer.exceptions import BasicAerError
from qiskit.providers.models import QasmBackendConfiguration
from .qasm_simulator import QasmSimulatorPy
Expand All @@ -36,12 +34,10 @@
class StatevectorSimulatorPy(QasmSimulatorPy):
"""Python statevector simulator."""

MAX_QUBITS_MEMORY = int(log2(local_hardware_info()["memory"] * (1024**3) / 16))

DEFAULT_CONFIGURATION = {
"backend_name": "statevector_simulator",
"backend_version": "1.1.0",
"n_qubits": min(24, MAX_QUBITS_MEMORY),
"n_qubits": 24,
"url": "https://github.com/Qiskit/qiskit-terra",
"simulator": True,
"local": True,
Expand Down
10 changes: 3 additions & 7 deletions qiskit/providers/basicaer/unitary_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,18 @@
UnitarySimulator().run(run_input)
Where the input is a either Qobj object (deprecated) or QuantumCircuit or a list of circuits and
the output is a BasicAerJob object, which can later be queried for the Result object. The result
Where the input is a either Qobj object (deprecated) or QuantumCircuit or a list of circuits and
the output is a BasicAerJob object, which can later be queried for the Result object. The result
will contain a 'unitary' data field, which is a 2**n x 2**n complex numpy array representing the
circuit's unitary matrix.
"""
import logging
import uuid
import time
from math import log2, sqrt
import warnings

import numpy as np

from qiskit.utils.multiprocessing import local_hardware_info
from qiskit.providers.models import QasmBackendConfiguration
from qiskit.providers.backend import BackendV1
from qiskit.providers.options import Options
Expand All @@ -54,12 +52,10 @@
class UnitarySimulatorPy(BackendV1):
"""Python implementation of a unitary simulator."""

MAX_QUBITS_MEMORY = int(log2(sqrt(local_hardware_info()["memory"] * (1024**3) / 16)))

DEFAULT_CONFIGURATION = {
"backend_name": "unitary_simulator",
"backend_version": "1.1.0",
"n_qubits": min(24, MAX_QUBITS_MEMORY),
"n_qubits": 24,
"url": "https://github.com/Qiskit/qiskit-terra",
"simulator": True,
"local": True,
Expand Down
16 changes: 12 additions & 4 deletions qiskit/utils/multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@

import multiprocessing as mp
import platform

import psutil
import os


def local_hardware_info():
Expand All @@ -27,13 +26,22 @@ def local_hardware_info():
Returns:
dict: The hardware information.
"""

if hasattr(os, "sched_getaffinity"):
num_cpus = len(os.sched_getaffinity(0))
else:
num_cpus = os.cpu_count()
if num_cpus is None:
num_cpus = 1
else:
num_cpus = int(num_cpus / 2) or 1

results = {
"python_compiler": platform.python_compiler(),
"python_build": ", ".join(platform.python_build()),
"python_version": platform.python_version(),
"os": platform.system(),
"memory": psutil.virtual_memory().total / (1024**3),
"cpus": psutil.cpu_count(logical=False) or 1,
"cpus": num_cpus,
}
return results

Expand Down
14 changes: 14 additions & 0 deletions releasenotes/notes/psutil-dependancy-removed-bf5366f516d92378.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
upgrade:
- |
The dependency on `psutil <https://pypi.org/project/psutil/>`__ has been removed. The
psutil library was previously only used for detecting the number of physical CPUs and
total system memory, however this information provided does not add sufficient value
to justify the additional dependencies and overhead so it has been removed. This does
mean that the default number of processes used by :func:`.parallel_map` and functions that
internally can use :func:`.parallel_map` such as :func:`.transpile` and :meth:`.PassManager.run`
may use more or less parallel processes than in previous releases. If you'd like to adjust the
number of processes used you can use the ``QISKIT_NUM_PROCS`` environment variable or
``num_processes`` field in a user configuration file (see the
`local configuration guide <https://docs.quantum.ibm.com/start/configure-qiskit-local>`__
for more details) if you need to adjust the number of processes that Qiskit potentially uses.
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
rustworkx>=0.13.0
numpy>=1.17,<2
psutil>=5
scipy>=1.5
sympy>=1.3
dill>=0.3
Expand Down
50 changes: 43 additions & 7 deletions test/python/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,54 @@

from unittest import mock

from qiskit.utils.multiprocessing import local_hardware_info
from qiskit.utils import multiprocessing
from qiskit.test import QiskitTestCase


class TestUtil(QiskitTestCase):
"""Tests for qiskit/_util.py"""

@mock.patch("platform.system", return_value="Linux")
@mock.patch("psutil.virtual_memory")
@mock.patch("psutil.cpu_count", return_value=None)
def test_local_hardware_none_cpu_count(self, cpu_count_mock, vmem_mock, platform_mock):
def test_local_hardware_five_cpu_count(self):
"""Test cpu count is half when sched affinity is 5"""
with mock.patch.object(multiprocessing, "os"):
multiprocessing.os.sched_getaffinity = mock.MagicMock(return_value=set(range(5)))
result = multiprocessing.local_hardware_info()
self.assertEqual(2, result["cpus"])

def test_local_hardware_sixty_four_cpu_count(self):
"""Test cpu count is 32 when sched affinity is 64"""
with mock.patch.object(multiprocessing, "os"):
multiprocessing.os.sched_getaffinity = mock.MagicMock(return_value=set(range(64)))
result = multiprocessing.local_hardware_info()
self.assertEqual(32, result["cpus"])

def test_local_hardware_no_cpu_count(self):
"""Test cpu count fallback to 1 when true value can't be determined"""
del cpu_count_mock, vmem_mock, platform_mock # unused
result = local_hardware_info()
with mock.patch.object(multiprocessing, "os"):
multiprocessing.os.sched_getaffinity = mock.MagicMock(return_value=set())
result = multiprocessing.local_hardware_info()
self.assertEqual(1, result["cpus"])

def test_local_hardware_no_sched_five_count(self):
"""Test cpu cound if sched affinity method is missing and cpu count is 5."""
with mock.patch.object(multiprocessing, "os", spec=[]):
multiprocessing.os.cpu_count = mock.MagicMock(return_value=5)
del multiprocessing.os.sched_getaffinity
result = multiprocessing.local_hardware_info()
self.assertEqual(2, result["cpus"])

def test_local_hardware_no_sched_sixty_four_count(self):
"""Test cpu cound if sched affinity method is missing and cpu count is 64."""
with mock.patch.object(multiprocessing, "os", spec=[]):
multiprocessing.os.cpu_count = mock.MagicMock(return_value=64)
del multiprocessing.os.sched_getaffinity
result = multiprocessing.local_hardware_info()
self.assertEqual(32, result["cpus"])

def test_local_hardware_no_sched_no_count(self):
"""Test cpu count fallback to 1 when no sched getaffinity available."""
with mock.patch.object(multiprocessing, "os", spec=[]):
multiprocessing.os.cpu_count = mock.MagicMock(return_value=None)
del multiprocessing.os.sched_getaffinity
result = multiprocessing.local_hardware_info()
self.assertEqual(1, result["cpus"])
2 changes: 1 addition & 1 deletion test/python/transpiler/test_mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ class TestsSabreSwap(SwapperCommonTestCases, QiskitTestCase):
"""Test SwapperCommonTestCases using SabreSwap."""

pass_class = SabreSwap
additional_args = {"seed": 1242}
additional_args = {"seed": 1242, "trials": 2}


if __name__ == "__main__":
Expand Down

0 comments on commit b5aaffb

Please sign in to comment.