Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,6 @@ cython_debug/
.vscode/

# Mac Os
.DS_Store
.DS_Store

config.toml
40 changes: 40 additions & 0 deletions configs/config_app_example.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# MAKE SURE TO RENAME THIS FILE TO config.toml AND PLACE IT IN THE ROOT OF THE PROJECT

# Router configuration
router_name = "router1"
router_address = "xx.xx.xx.xx" # Replace with actual IP address
router_port = 5555

# Timetagger configuration (provider_name, instrument_name)
timetagger = ["provider", "tagger"] # Replace with actual provider and instrument names

# Bell state configuration (default: Phi_plus)
bell_state = 0

# CHSH experiment settings
[chsh_settings]
hwp = ["provider", "instrument_hwp"] # Replace with actual HWP names
request_hwp = ["provider", "instrument_hwp"]

# CHSH measurement configuration
[chsh_settings.measurement_config]
integration_time_s= 5
binwidth = 500
channel1 = 1
channel2 = 2
dark_count = 0

# QKD experiment settings
[qkd_settings]
hwp = ["provider", "instrument_hwp"]
request_hwp = ["provider", "instrument_hwp"]
bitstring_length = 4
discriminating_threshold = 10

# QKD measurement configuration
[qkd_settings.measurement_config]
integration_time_s = 5
binwidth = 500
channel1 = 1
channel2 = 2
dark_count = 0
23 changes: 23 additions & 0 deletions configs/config_messaging_example.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[router]
name = "pqnstack-router"
host = "localhost"
port = 5556

[provider]
name = "pqnstack-provider"
router_name = "pqnstack-router"
host = "localhost"
port = 5556
beat_period = 2000

[[provider.instruments]]
name = "dummy1"
import = "pqnstack.pqn.drivers.dummies.DummyInstrument"
desc = "Dummy instrument1 for testing purposes"
hw_address = "1234"

[[provider.instruments]]
name = "dummy2"
import = "pqnstack.pqn.drivers.dummies.DummyInstrument"
desc = "Dummy instrument2 for testing purposes"
hw_address = "1234"
28 changes: 0 additions & 28 deletions configs/pqn-node.json

This file was deleted.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pqn = "pqnstack.cli:app"
webapp = [
"fastapi[standard]>=0.115.14",
"httpx>=0.28.1",
"pydantic>=2.11.7",
"pydantic-settings>=2.10.1",
]

[dependency-groups]
Expand Down
60 changes: 39 additions & 21 deletions src/pqnstack/app/core/config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import logging
from dataclasses import dataclass
from dataclasses import field
from functools import lru_cache

from pydantic import BaseModel
from pydantic import Field
from pydantic_settings import BaseSettings
from pydantic_settings import PydanticBaseSettingsSource
from pydantic_settings import SettingsConfigDict
from pydantic_settings import TomlConfigSettingsSource

from pqnstack.constants import BellState
from pqnstack.constants import QKDEncodingBasis
Expand All @@ -11,39 +15,53 @@
logger = logging.getLogger(__name__)


@dataclass
class CHSHSettings:
class CHSHSettings(BaseModel):
# Specifies which half waveplate to use for the CHSH experiment. First value is the provider's name, second is the motor name.
hwp: tuple[str, str] = ("", "")
request_hwp: tuple[str, str] = ("", "")
measurement_config: MeasurementConfig = field(default_factory=lambda: MeasurementConfig(5))
measurement_config: MeasurementConfig = Field(default_factory=lambda: MeasurementConfig(integration_time_s=5))


@dataclass
class QKDSettings:
class QKDSettings(BaseModel):
hwp: tuple[str, str] = ("", "")
request_hwp: tuple[str, str] = ("", "")
bitstring_length: int = 4
discriminating_threshold = 10
measurement_config: MeasurementConfig = field(default_factory=lambda: MeasurementConfig(5))
discriminating_threshold: int = 10
measurement_config: MeasurementConfig = Field(default_factory=lambda: MeasurementConfig(integration_time_s=5))


@dataclass
class Settings:
router_name: str
router_address: str
router_port: int
chsh_settings: CHSHSettings
qkd_settings: QKDSettings
class Settings(BaseSettings):
router_name: str = "router1"
router_address: str = "localhost"
router_port: int = 5555
chsh_settings: CHSHSettings = CHSHSettings()
qkd_settings: QKDSettings = QKDSettings()
bell_state: BellState = BellState.Phi_plus
timetagger: tuple[str, str] | None = None # Name of the timetagger to use for the CHSH experiment.


static_typecheck_msg = "Please set the global 'settings' variable before use."


model_config = SettingsConfigDict(toml_file="./config.toml", env_file=".env", env_file_encoding="utf-8")

@classmethod
def settings_customise_sources(
cls,
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> tuple[PydanticBaseSettingsSource, ...]:
return (
TomlConfigSettingsSource(settings_cls),
env_settings,
dotenv_settings,
file_secret_settings,
init_settings,
)


@lru_cache
def get_settings() -> Settings:
raise NotImplementedError(static_typecheck_msg)
return Settings()
Comment on lines 63 to +64

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are simply importing the settings object and using it there, the get_settings() only gets called once already. I don't think most people know about that function so I don't want to add more complexity for basically no new functionality

@Benjamin-Nussbaum Benjamin-Nussbaum Aug 18, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have dependencies with other endpoints in addition to the config, it might add to the complexity of why one is a (Annotated) Depends dpendency and another is directly imported. If we want to directly import, the get_settings() function itself is unnecessary; we can just instantiate the Settings object at the module level directly. But it makes more sense to me to stick to the FastAPI standard using Depends, in which case I also recommend using @lru_cache.



settings = get_settings()
Expand Down
5 changes: 3 additions & 2 deletions src/pqnstack/pqn/protocols/measurement.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from dataclasses import dataclass

from pydantic import BaseModel

@dataclass
class MeasurementConfig:

class MeasurementConfig(BaseModel):
integration_time_s: float
binwidth_ps: int = 500
channel1: int = 1
Expand Down
Loading