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
5 changes: 4 additions & 1 deletion docs/_extend_docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import inspect
import logging
import textwrap
from typing import Callable
from typing import TYPE_CHECKING

import qrules

if TYPE_CHECKING:
from collections.abc import Callable

logging.getLogger().setLevel(logging.ERROR)


Expand Down
2 changes: 1 addition & 1 deletion docs/usage.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.18"
"version": "3.13.8"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/conservation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
"version": "3.13.8"
}
},
"nbformat": 4,
Expand Down
12 changes: 12 additions & 0 deletions docs/usage/custom-topology.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,18 @@
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.8"
}
},
"nbformat": 4,
Expand Down
12 changes: 12 additions & 0 deletions docs/usage/ls-coupling.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,18 @@
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.8"
}
},
"nbformat": 4,
Expand Down
12 changes: 12 additions & 0 deletions docs/usage/particle.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,18 @@
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.8"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/reaction.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.11"
"version": "3.13.8"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/visualize.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.8"
"version": "3.13.8"
}
},
"nbformat": 4,
Expand Down
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python",
"Topic :: Scientific/Engineering :: Physics",
"Topic :: Scientific/Engineering",
Expand Down Expand Up @@ -49,7 +48,7 @@ keywords = [
license = {file = "LICENSE"}
maintainers = [{email = "[email protected]"}]
name = "qrules"
requires-python = ">=3.9"
requires-python = ">=3.10"

[project.optional-dependencies]
viz = ["graphviz"]
Expand Down Expand Up @@ -303,6 +302,11 @@ env = {EXECUTE_NB = "yes"}
help = "Set up a server to directly preview changes to the HTML pages with cached notebook execution"
sequence = ["doclive"]

[tool.poe.tasks.lab]
args = [{name = "paths", default = "", positional = true}]
cmd = "jupyter lab ${paths}"
help = "Run Jupyter Lab"

[tool.poe.tasks.linkcheck]
cmd = """
uv run --group doc --isolated --no-dev \
Expand Down
13 changes: 6 additions & 7 deletions src/qrules/argument_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import inspect
from fractions import Fraction
from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, Union
from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union

import attrs

Expand All @@ -21,11 +21,10 @@
from qrules.quantum_numbers import EdgeQuantumNumber, NodeQuantumNumber, Parity

if TYPE_CHECKING:
from collections.abc import Sequence
from collections.abc import Callable, Sequence

Scalar = Union[int, float, Fraction]

Rule = Union[GraphElementRule, EdgeQNConservationRule, ConservationRule]
Scalar = int | float | Fraction
Rule = GraphElementRule | EdgeQNConservationRule | ConservationRule
"""Any type of rule"""

_ElementType = TypeVar("_ElementType")
Expand Down Expand Up @@ -95,7 +94,7 @@ def wrapper(states_list: Sequence[Any]) -> bool:

def _check_all_arguments(checks: list[Callable]) -> Callable[..., bool]:
def wrapper(*args: Any) -> bool:
return all(check(arg) for check, arg in zip(checks, args))
return all(check(arg) for check, arg in zip(checks, args, strict=False))

return wrapper

Expand Down Expand Up @@ -171,7 +170,7 @@ def wrapper(states_list: Sequence[Any]) -> list[Any]:

def _build_all_arguments(checks: list[Callable]) -> Callable:
def wrapper(*args: Any) -> list[Any]:
return [check(arg) for check, arg in zip(checks, args) if arg]
return [check(arg) for check, arg in zip(checks, args, strict=False) if arg]

return wrapper

Expand Down
21 changes: 13 additions & 8 deletions src/qrules/combinatorics.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

import itertools
from collections import OrderedDict
from collections.abc import Iterable, Mapping, Sequence
from collections.abc import Callable, Iterable, Mapping, Sequence
from copy import deepcopy
from fractions import Fraction
from typing import TYPE_CHECKING, Any, Callable, Union
from typing import TYPE_CHECKING, Any

from qrules.argument_handling import Scalar
from qrules.particle import ParticleWithSpin
Expand All @@ -24,9 +24,9 @@


StateWithSpins = tuple[str, Sequence[Fraction]]
StateDefinition = Union[str, StateWithSpins]
StateDefinition = str | StateWithSpins
"""Particle name, optionally with a list of spin projections."""
StateDefinitionInput = Union[str, tuple[str, Sequence[Scalar]]]
StateDefinitionInput = str | tuple[str, Sequence[Scalar]]
"""Input type for `StateDefinition` permitting also `int` and `float`"""
InitialFacts = MutableTransition[ParticleWithSpin, InteractionProperties]
"""A `.Transition` with only initial and final state information."""
Expand Down Expand Up @@ -221,7 +221,7 @@ def __create_states_with_spin_projections(
msg = "Number of state definitions is not same as number of edge IDs"
raise ValueError(msg)
states = __safe_set_spin_projections(state_definitions, particle_db)
return dict(zip(edge_ids, states))
return dict(zip(edge_ids, states, strict=True))


def __safe_set_spin_projections(
Expand Down Expand Up @@ -289,7 +289,9 @@ def strip_spin(state: StateDefinitionInput) -> str:
states = list(initial_state) + list(final_state)
return _generate_kinematic_permutations(
topology,
particle_names={i: strip_spin(s) for i, s in zip(edge_ids, states)},
particle_names={
i: strip_spin(s) for i, s in zip(edge_ids, states, strict=True)
},
allowed_kinematic_groupings=__get_kinematic_groupings(final_state_groupings),
)

Expand Down Expand Up @@ -331,6 +333,7 @@ def _permutate_outer_edges(topology: Topology) -> list[Topology]:
permutation = zip(
initial_state_ids + final_state_ids,
initial_state_permutation + final_state_permutation,
strict=True,
)
new_topology = topology.relabel_edges(dict(permutation))
topologies.add(new_topology)
Expand Down Expand Up @@ -460,10 +463,12 @@ def _external_edge_identical_particle_combinatorics(
ext_edge_combinations = []
ref_node_origin = get_originating_node_list(graph.topology, edge_group)
for comb in combinations:
temp_edge_node_mapping = tuple(sorted(zip(comb, ref_node_origin)))
temp_edge_node_mapping = tuple(
sorted(zip(comb, ref_node_origin, strict=False))
)
if temp_edge_node_mapping not in graph_combinations:
graph_combinations.add(temp_edge_node_mapping)
ext_edge_combinations.append(dict(zip(edge_group, comb)))
ext_edge_combinations.append(dict(zip(edge_group, comb, strict=True)))
temp_new_graphs = []
for new_graph in new_graphs:
for combination in ext_edge_combinations:
Expand Down
43 changes: 22 additions & 21 deletions src/qrules/conservation_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@
"""

import operator
from collections.abc import Callable
from copy import deepcopy
from fractions import Fraction
from functools import reduce
from textwrap import dedent
from typing import Any, Callable, Optional, Protocol, Union
from typing import Any, Protocol

from attrs import define, field, frozen
from attrs.converters import optional
Expand Down Expand Up @@ -232,7 +233,7 @@ def parity_conservation_helicity(
class CParityEdgeInput:
spin_magnitude: EdgeQN.spin_magnitude = field(converter=to_fraction)
pid: EdgeQN.pid = field(converter=int)
c_parity: Optional[EdgeQN.c_parity] = field(
c_parity: EdgeQN.c_parity | None = field(
converter=optional(to_parity), default=None
)

Expand All @@ -256,7 +257,7 @@ def c_parity_conservation(

def _get_c_parity_multiparticle(
part_qns: list[CParityEdgeInput], interaction_qns: CParityNodeInput
) -> Optional[int]:
) -> int | None:
c_parities_part = [x.c_parity.value for x in part_qns if x.c_parity]
# if all states have C parity defined, then just multiply them
if len(c_parities_part) == len(part_qns):
Expand Down Expand Up @@ -290,7 +291,7 @@ class GParityEdgeInput:
isospin_magnitude: EdgeQN.isospin_magnitude = field(converter=to_fraction)
spin_magnitude: EdgeQN.spin_magnitude = field(converter=to_fraction)
pid: EdgeQN.pid = field(converter=int)
g_parity: Optional[EdgeQN.g_parity] = field(
g_parity: EdgeQN.g_parity | None = field(
converter=optional(to_parity), default=None
)

Expand All @@ -314,7 +315,7 @@ def g_parity_conservation( # noqa: C901
def check_multistate_g_parity(
isospin: EdgeQN.isospin_magnitude,
double_state_qns: tuple[GParityEdgeInput, GParityEdgeInput],
) -> Optional[int]:
) -> int | None:
if _is_particle_antiparticle_pair(
double_state_qns[0].pid, double_state_qns[1].pid
):
Expand Down Expand Up @@ -434,8 +435,8 @@ def _check_particles_identical(

@frozen
class _Spin:
magnitude: Union[Fraction, NodeQN.s_magnitude, NodeQN.l_magnitude]
projection: Union[Fraction, NodeQN.s_projection, NodeQN.l_projection]
magnitude: Fraction | NodeQN.s_magnitude | NodeQN.l_magnitude
projection: Fraction | NodeQN.s_projection | NodeQN.l_projection


def _is_clebsch_gordan_coefficient_zero(
Expand Down Expand Up @@ -480,7 +481,7 @@ def ls_spin_validity(spin_input: SpinNodeInput) -> bool:
def _check_magnitude(
in_part: list[Fraction],
out_part: list[Fraction],
interaction_qns: Optional[Union[SpinMagnitudeNodeInput, SpinNodeInput]],
interaction_qns: SpinMagnitudeNodeInput | SpinNodeInput | None,
) -> bool:
def couple_mags(j_1: Fraction, j_2: Fraction) -> list[Fraction]:
return [
Expand All @@ -490,7 +491,7 @@ def couple_mags(j_1: Fraction, j_2: Fraction) -> list[Fraction]:

def couple_magnitudes(
magnitudes: list[Fraction],
interaction_qns: Optional[Union[SpinMagnitudeNodeInput, SpinNodeInput]],
interaction_qns: SpinMagnitudeNodeInput | SpinNodeInput | None,
) -> set[Fraction]:
if len(magnitudes) == 1:
return set(magnitudes)
Expand Down Expand Up @@ -523,7 +524,7 @@ def couple_magnitudes(
def _check_spin_couplings(
in_part: list[_Spin],
out_part: list[_Spin],
interaction_qns: Optional[SpinNodeInput],
interaction_qns: SpinNodeInput | None,
) -> bool:
in_tot_spins = __calculate_total_spins(in_part, interaction_qns)
out_tot_spins = __calculate_total_spins(out_part, interaction_qns)
Expand All @@ -533,7 +534,7 @@ def _check_spin_couplings(

def __calculate_total_spins(
spins: list[_Spin],
interaction_qns: Optional[SpinNodeInput] = None,
interaction_qns: SpinNodeInput | None = None,
) -> set[_Spin]:
total_spins = set()
if len(spins) == 1:
Expand Down Expand Up @@ -785,31 +786,31 @@ def helicity_conservation(
@frozen
class GellMannNishijimaInput:
charge: EdgeQN.charge = field(converter=EdgeQN.charge)
isospin_projection: Optional[EdgeQN.isospin_projection] = field(
isospin_projection: EdgeQN.isospin_projection | None = field(
converter=optional(EdgeQN.isospin_projection), default=None
)
strangeness: Optional[EdgeQN.strangeness] = field(
strangeness: EdgeQN.strangeness | None = field(
converter=optional(EdgeQN.strangeness), default=None
)
charmness: Optional[EdgeQN.charmness] = field(
charmness: EdgeQN.charmness | None = field(
converter=optional(EdgeQN.charmness), default=None
)
bottomness: Optional[EdgeQN.bottomness] = field(
bottomness: EdgeQN.bottomness | None = field(
converter=optional(EdgeQN.bottomness), default=None
)
topness: Optional[EdgeQN.topness] = field(
topness: EdgeQN.topness | None = field(
converter=optional(EdgeQN.topness), default=None
)
baryon_number: Optional[EdgeQN.baryon_number] = field(
baryon_number: EdgeQN.baryon_number | None = field(
converter=optional(EdgeQN.baryon_number), default=None
)
electron_lepton_number: Optional[EdgeQN.electron_lepton_number] = field(
electron_lepton_number: EdgeQN.electron_lepton_number | None = field(
converter=optional(EdgeQN.electron_lepton_number), default=None
)
muon_lepton_number: Optional[EdgeQN.muon_lepton_number] = field(
muon_lepton_number: EdgeQN.muon_lepton_number | None = field(
converter=optional(EdgeQN.muon_lepton_number), default=None
)
tau_lepton_number: Optional[EdgeQN.tau_lepton_number] = field(
tau_lepton_number: EdgeQN.tau_lepton_number | None = field(
converter=optional(EdgeQN.tau_lepton_number), default=None
)

Expand Down Expand Up @@ -863,7 +864,7 @@ def calculate_hypercharge(
@frozen
class MassEdgeInput:
mass: EdgeQN.mass = field(converter=EdgeQN.mass)
width: Optional[EdgeQN.width] = field(converter=EdgeQN.width, default=None)
width: EdgeQN.width | None = field(converter=EdgeQN.width, default=None)


class MassConservation:
Expand Down
Loading
Loading