Skip to content

Commit

Permalink
Merge branch 'main' into noise_amplifier
Browse files Browse the repository at this point in the history
  • Loading branch information
merav-aharoni authored Sep 19, 2023
2 parents 79ddbfc + ed3f533 commit 2ab7324
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 109 deletions.
67 changes: 57 additions & 10 deletions qiskit_ibm_runtime/accounts/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def save(
verify: Optional[bool] = None,
overwrite: Optional[bool] = False,
channel_strategy: Optional[str] = None,
set_as_default: Optional[bool] = None,
) -> None:
"""Save account on disk."""
channel = channel or os.getenv("QISKIT_IBM_CHANNEL") or _DEFAULT_CHANNEL_TYPE
Expand All @@ -69,6 +70,7 @@ def save(
)
# avoid storing invalid accounts
.validate().to_saved_format(),
set_as_default=set_as_default,
)

@staticmethod
Expand Down Expand Up @@ -137,6 +139,21 @@ def get(
filename: Full path of the file from which to get the account.
name: Account name.
channel: Channel type.
Order of precedence for selecting the account:
1. If name is specified, get account with that name
2. If the environment variables define an account, get that one
3. If the channel parameter is defined,
a. get the account of this channel type defined as "is_default_account"
b. get the account of this channel type with default name
c. get any account of this channel type
4. If the channel is defined in "QISKIT_IBM_CHANNEL"
a. get the account of this channel type defined as "is_default_account"
b. get the account of this channel type with default name
c. get any account of this channel type
5. If a default account is defined in the json file, get that account
6. Get any account that is defined in the json file with
preference for _DEFAULT_CHANNEL_TYPE.
Returns:
Account information.
Expand All @@ -157,18 +174,20 @@ def get(
if env_account is not None:
return env_account

if channel:
saved_account = read_config(
filename=filename,
name=cls._get_default_account_name(channel=channel),
)
if saved_account is None:
if os.path.isfile(_QISKITRC_CONFIG_FILE):
return cls._from_qiskitrc_file()
raise AccountNotFoundError(f"No default {channel} account saved.")
all_config = read_config(filename=filename)
# Get the default account for the given channel.
# If channel == None, get the default account, for any channel, if it exists
saved_account = cls._get_default_account(all_config, channel)

if saved_account is not None:
return Account.from_saved_format(saved_account)

all_config = read_config(filename=filename)
# Get the default account from the channel defined in the environment variable
account = cls._get_default_account(all_config, channel=channel_)
if account is not None:
return Account.from_saved_format(account)

# check for any account
for channel_type in _CHANNEL_TYPES:
account_name = cls._get_default_account_name(channel=channel_type)
if account_name in all_config:
Expand Down Expand Up @@ -209,6 +228,34 @@ def _from_env_variables(cls, channel: Optional[ChannelType]) -> Optional[Account
channel=channel,
)

@classmethod
def _get_default_account(
cls, all_config: dict, channel: Optional[str] = None
) -> Optional[dict]:
default_channel_account = None
any_channel_account = None

for account_name in all_config:
account = all_config[account_name]
if channel:
if account.get("channel") == channel and account.get("is_default_account"):
return account
if account.get(
"channel"
) == channel and account_name == cls._get_default_account_name(channel):
default_channel_account = account
if account.get("channel") == channel:
any_channel_account = account
else:
if account.get("is_default_account"):
return account

if default_channel_account:
return default_channel_account
elif any_channel_account:
return any_channel_account
return None

@classmethod
def _get_default_account_name(cls, channel: ChannelType) -> str:
return (
Expand Down
22 changes: 20 additions & 2 deletions qiskit_ibm_runtime/accounts/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
logger = logging.getLogger(__name__)


def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None:
def save_config(
filename: str, name: str, config: dict, overwrite: bool, set_as_default: Optional[bool] = None
) -> None:
"""Save configuration data in a JSON file under the given name."""
logger.debug("Save configuration data for '%s' in '%s'", name, filename)
_ensure_file_exists(filename)
Expand All @@ -35,8 +37,24 @@ def save_config(filename: str, name: str, config: dict, overwrite: bool) -> None
f"Named account ({name}) already exists. " f"Set overwrite=True to overwrite."
)

data[name] = config

# if set_as_default, but another account is defined as default, user must specify overwrite to change
# the default account.
if set_as_default:
data[name]["is_default_account"] = True
for account_name in data:
account = data[account_name]
if account_name != name and account.get("is_default_account"):
if overwrite:
del account["is_default_account"]
else:
raise AccountAlreadyExistsError(
f"default_account ({name}) already exists. "
f"Set overwrite=True to overwrite."
)

with open(filename, mode="w", encoding="utf-8") as json_out:
data[name] = config
json.dump(data, json_out, sort_keys=True, indent=4)


Expand Down
3 changes: 2 additions & 1 deletion qiskit_ibm_runtime/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"""Exceptions related to the IBM Runtime service."""

from qiskit.exceptions import QiskitError
from qiskit.providers.exceptions import JobTimeoutError


class IBMError(QiskitError):
Expand Down Expand Up @@ -87,7 +88,7 @@ class RuntimeInvalidStateError(IBMRuntimeError):
pass


class RuntimeJobTimeoutError(IBMRuntimeError):
class RuntimeJobTimeoutError(JobTimeoutError):
"""Error raised when waiting for job times out."""

pass
Expand Down
6 changes: 6 additions & 0 deletions qiskit_ibm_runtime/qiskit_runtime_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def __init__(
- Account with the input `name`, if specified.
- Default account for the `channel` type, if `channel` is specified but `token` is not.
- Account defined by the input `channel` and `token`, if specified.
- Account defined by the `default_channel` if defined in filename
- Account defined by the environment variables, if defined.
- Default account for the ``ibm_cloud`` account, if one is available.
- Default account for the ``ibm_quantum`` account, if one is available.
Expand Down Expand Up @@ -287,6 +288,7 @@ def _discover_account(
"'channel' is required if 'token', or 'url' is specified but 'name' is not."
)

# channel is not defined yet, get it from the AccountManager
if account is None:
account = AccountManager.get(filename=filename)

Expand Down Expand Up @@ -689,6 +691,7 @@ def save_account(
verify: Optional[bool] = None,
overwrite: Optional[bool] = False,
channel_strategy: Optional[str] = None,
set_as_default: Optional[bool] = None,
) -> None:
"""Save the account to disk for future use.
Expand All @@ -709,6 +712,8 @@ def save_account(
verify: Verify the server's TLS certificate.
overwrite: ``True`` if the existing account is to be overwritten.
channel_strategy: Error mitigation strategy.
set_as_default: If ``True``, the account is saved in filename,
as the default account.
"""

AccountManager.save(
Expand All @@ -722,6 +727,7 @@ def save_account(
verify=verify,
overwrite=overwrite,
channel_strategy=channel_strategy,
set_as_default=set_as_default,
)

@staticmethod
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/default_account-13d86d50f5b1d972.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
features:
- |
Added the option to define a default account in the account json file.
The select an account as default, define ``set_as_default=True`` in
``QiskitRuntimeService.save_account()``.
3 changes: 3 additions & 0 deletions test/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def get_account_config_contents(
instance=None,
verify=None,
proxies=None,
set_default=None,
):
"""Generate qiskitrc content"""
if instance is None:
Expand All @@ -177,4 +178,6 @@ def get_account_config_contents(
out[name]["verify"] = verify
if proxies is not None:
out[name]["proxies"] = proxies
if set_default:
out[name]["is_default_account"] = True
return out
Loading

0 comments on commit 2ab7324

Please sign in to comment.