plm-physics 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plm_physics/__init__.py +1 -0
- plm_physics/claims.py +48 -0
- plm_physics/controller.py +44 -0
- plm_physics/levels.py +43 -0
- plm_physics/manifest.py +26 -0
- plm_physics-0.1.0.dist-info/METADATA +20 -0
- plm_physics-0.1.0.dist-info/RECORD +10 -0
- plm_physics-0.1.0.dist-info/WHEEL +4 -0
- plm_physics-0.1.0.dist-info/entry_points.txt +2 -0
- plm_physics-0.1.0.dist-info/licenses/LICENSE +21 -0
plm_physics/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Physics and mechanical pack: level rules, tiers, claims and unit words; the rest is plm.numeric."""
|
plm_physics/claims.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Which tasks the physics pack claims, and how strongly.
|
|
2
|
+
|
|
3
|
+
A physics calculation names something from mechanics, fluids, heat or materials
|
|
4
|
+
and gives a quantity (a number with a unit) or asks for a unit. Conceptual
|
|
5
|
+
questions ("Why does ice float?") have neither and go to the general pack.
|
|
6
|
+
|
|
7
|
+
Physics shares its quantities with the specialist packs: speed, mass, pressure
|
|
8
|
+
and density are in aerospace questions too, where aerospace counts a phrase like
|
|
9
|
+
"dynamic pressure" once while physics would count "density" and "pressure"
|
|
10
|
+
twice. So generic quantity words let physics claim a text but add nothing to
|
|
11
|
+
its score: only words that are physics's own (pendulum, friction, buoyant, ...)
|
|
12
|
+
count as evidence. A text with generic words only scores GENERIC_ONLY, below
|
|
13
|
+
any specialist's claim, so physics takes it only when no specialist does. Its
|
|
14
|
+
priority is the lowest of the numeric packs, so equal evidence goes to the
|
|
15
|
+
specialist too. Words that mean electricity (power, energy alone, wire,
|
|
16
|
+
frequency) are in neither list.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import re
|
|
20
|
+
|
|
21
|
+
from plm.numeric.checks import requested_unit
|
|
22
|
+
from plm.pack import evidence
|
|
23
|
+
|
|
24
|
+
GENERIC_ONLY = 0.5
|
|
25
|
+
GENERIC = re.compile(
|
|
26
|
+
r"\b(?:mass|weight|speed|velocity|acceleration|accelerates?|density|pressure|temperature|flow rate"
|
|
27
|
+
r"|height|distance)\b", re.IGNORECASE)
|
|
28
|
+
PHYSICS_TERM = re.compile(
|
|
29
|
+
r"\b(?:force|net force|decelerat\w*|momentum|kinetic energy|potential energy|elastic energy|work|friction"
|
|
30
|
+
r"|stiffness|spring(?: constant)?|pendulum|oscillat\w*|wavelength|waves?|torque|wrench|lever|flywheel"
|
|
31
|
+
r"|rotat\w*|angular|radians?|wheel|moment of inertia|rolls?|rolling|gears?|shaft|pulley|piston"
|
|
32
|
+
r"|gauge pressure|buoyan\w*|submerged|floats?|fluid|pipe|water|tank|ice|steam|heat|specific heat"
|
|
33
|
+
r"|latent heat|boil\w*|melt\w*|thermal|conductivity|expansion|ideal gas|moles?|stress|strain"
|
|
34
|
+
r"|young's modulus|tensile|cable|beam|collision|projectile|thrown|kicked|dropped|falls?|cliff|ramp"
|
|
35
|
+
r"|incline\w*|slope|crate|ball|machine|motor|elevator|crane|pump|lifts?|kettle|gravity|centripetal|tension"
|
|
36
|
+
r"|brakes?|stopping distance|air resistance)\b",
|
|
37
|
+
re.IGNORECASE)
|
|
38
|
+
QUANTITY = re.compile(r"\d\s*(?:m/s²|m/s\^?2|m/s|km/h|kg/m³|kg|g|N·m|N/m|N|kN|J|kJ|MJ|Pa|kPa|MPa|GPa|K|°C|m|cm|mm"
|
|
39
|
+
r"|km|m²|m³|s|min|h|L|rpm|mol|degrees?|%)(?![\w/])")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def claims(text):
|
|
43
|
+
"""Distinct physics terms (generic quantities don't count), when there is a quantity or a unit is asked for."""
|
|
44
|
+
if not (PHYSICS_TERM.search(text) or GENERIC.search(text)):
|
|
45
|
+
return 0
|
|
46
|
+
if not (QUANTITY.search(text) or requested_unit(text) is not None):
|
|
47
|
+
return 0
|
|
48
|
+
return evidence(PHYSICS_TERM, text) or GENERIC_ONLY
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Physics pack: the 4B with the calculator for calm and alert problems, escalating to the 8B.
|
|
2
|
+
|
|
3
|
+
On the 50-problem eval set, PHYSICS_RULE (evals/simulate_routing.py, fixed before that
|
|
4
|
+
sweep) shipped always 8B answering directly: 49/50, while the calculator lost points
|
|
5
|
+
there by printing base-unit values under prefixed units ("5e+04 MPa" for 50 MPa).
|
|
6
|
+
|
|
7
|
+
SciBench's physics problems (evals/bench_physics.py: Halliday and classical mechanics,
|
|
8
|
+
144, written by others; PHYS_RULE, accuracy first, fixed before the runs) reversed that:
|
|
9
|
+
|
|
10
|
+
4B+calc | 8B+calc, escalating (now) 69/144 (48%) at 48% of always-8B-direct compute
|
|
11
|
+
always 8B + calculator 68/144 at 45%
|
|
12
|
+
always 4B + calculator 63/144 at 24%
|
|
13
|
+
always 8B, direct (shipped before) 52/144 (36%) at 100%
|
|
14
|
+
|
|
15
|
+
The routed policy beats always-8B-direct by +4 to +30 problems (paired 95%), at half the
|
|
16
|
+
compute: it is the electrical pack's shape. The check caught 19 of 76-94 wrong answers
|
|
17
|
+
per strategy. Budgets cover the longest calculator answers seen; 5-6 of 144 hit 1024.
|
|
18
|
+
|
|
19
|
+
Model names can be overridden with PLM_SMALL_MODEL and PLM_LARGE_MODEL.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import os
|
|
23
|
+
|
|
24
|
+
from plm.numeric import controller as numeric
|
|
25
|
+
|
|
26
|
+
from plm_physics.levels import classify
|
|
27
|
+
|
|
28
|
+
MODELS = {
|
|
29
|
+
"small": os.environ.get("PLM_SMALL_MODEL", "hf.co/bartowski/Qwen_Qwen3-4B-GGUF:Q4_K_M"),
|
|
30
|
+
"large": os.environ.get("PLM_LARGE_MODEL", "qwen3:8b"),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Per level: token budget and the escalation chain of (model, strategy) steps.
|
|
34
|
+
LEVELS = {
|
|
35
|
+
"calm": {"num_predict": 1024, "chain": [("small", "tool"), ("large", "tool")]},
|
|
36
|
+
"alert": {"num_predict": 1024, "chain": [("small", "tool"), ("large", "tool")]},
|
|
37
|
+
"intensive": {"num_predict": 1536, "chain": [("large", "tool"), ("large", "bare")]},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def solve(task, level=None, timeout=300):
|
|
42
|
+
"""Answer one physics task. Returns the answer plus a record of every model call."""
|
|
43
|
+
return numeric.solve(task, pack="physics", classify=classify, levels=LEVELS, models=MODELS,
|
|
44
|
+
level=level, timeout=timeout)
|
plm_physics/levels.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Physics pack: chain cues for the shared numeric level rules (plm/numeric/levels.py).
|
|
2
|
+
|
|
3
|
+
Each family marks a problem where an intermediate result has to be computed and
|
|
4
|
+
reused. Written from mechanics, fluids and heat practice after the eval set and
|
|
5
|
+
the holdout (evals/physics/holdout_levels.jsonl) were written; the holdout is
|
|
6
|
+
never used to tune them. Evaluate them with:
|
|
7
|
+
.venv/bin/python evals/eval_levels.py --pack physics
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from plm.numeric import levels as numeric_levels
|
|
11
|
+
|
|
12
|
+
CHAIN_CUES = {
|
|
13
|
+
# flight time first, then range or landing point
|
|
14
|
+
"projectile": r"\b(?:thrown|kicked|rolls off|fired)\b.*\b(?:land|lands|returns?|how high|how far)\b"
|
|
15
|
+
r"|horizontally from|\bin the air\b",
|
|
16
|
+
# an energy balance or an acceleration first, then the speed, height or distance
|
|
17
|
+
"energy-chain": r"from rest down|at the bottom|just before it hits|how high does .* swing|before stopping"
|
|
18
|
+
r"|highest speed|brakes? (?:hard|from)",
|
|
19
|
+
# momentum conserved first, then the energy or the motion after
|
|
20
|
+
"collision": r"\bhits? a (?:stationary|parked)|move off together|stick together|embeds? itself|energy is lost",
|
|
21
|
+
# moment of inertia and angular speed first, then the energy
|
|
22
|
+
"rotation-energy": r"flywheel|rolls? without slipping|moment of inertia|\bsolid (?:disc|sphere|cylinder)\b"
|
|
23
|
+
r"|\bhollow (?:cylinder|sphere)\b",
|
|
24
|
+
# continuity or Torricelli first, then pressure or flow
|
|
25
|
+
"fluid-chain": r"\bnarrows\b|bernoulli|continuity|drains? .* through a hole|outflow rate"
|
|
26
|
+
r"|volume of the .* (?:above|below) the water",
|
|
27
|
+
# a heat balance or a phase change, then a temperature or a time
|
|
28
|
+
"heat-chain": r"\bmixed\b|final temperature|\bice\b.*\binto water\b|how long does it take|in one hour"
|
|
29
|
+
r"|internal energy|heated at constant|thermal stress",
|
|
30
|
+
# a friction or banking limit first, then the speed it allows
|
|
31
|
+
"curve-limit": r"\bcurve\b.*\b(?:highest|maximum|top) speed|(?:highest|maximum|top) speed .*\bcurve\b|\bbanked\b",
|
|
32
|
+
# an area or a force from given sizes, then stress or extension
|
|
33
|
+
"materials-chain": r"diameter of .* (?:holds|carries|load)|clamped between",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def features(text):
|
|
38
|
+
return numeric_levels.features(text, CHAIN_CUES)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def classify(text):
|
|
42
|
+
"""Return (level, reason)."""
|
|
43
|
+
return numeric_levels.classify(text, CHAIN_CUES)
|
plm_physics/manifest.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""The physics pack's manifest: what the router needs to know about it."""
|
|
2
|
+
|
|
3
|
+
from plm.pack import Pack
|
|
4
|
+
|
|
5
|
+
from plm_physics import controller
|
|
6
|
+
from plm_physics.claims import claims
|
|
7
|
+
|
|
8
|
+
# Mechanics, fluids, heat and materials. Words other packs also declare (newtons,
|
|
9
|
+
# pascals, meters per second, ...) are repeated here so this pack works without them;
|
|
10
|
+
# a word two packs declare must mean the same unit.
|
|
11
|
+
UNIT_WORDS = {
|
|
12
|
+
"newtons": "N", "kilonewtons": "kN", "newton-meters": "N*m", "newtons per meter": "N/m",
|
|
13
|
+
"newton-seconds": "N*s", "kilogram meters per second": "kg*m/s",
|
|
14
|
+
"meters per second": "m/s", "kilometers per hour": "km/h", "meters per second squared": "m/s^2",
|
|
15
|
+
"radians per second": "rad/s", "revolutions per minute": "rpm", "hertz": "Hz",
|
|
16
|
+
"centimeters": "cm", "millimeters": "mm", "cubic meters": "m^3", "liters": "L",
|
|
17
|
+
"liters per second": "L/s", "cubic meters per second": "m^3/s",
|
|
18
|
+
"grams": "g", "kilograms per cubic meter": "kg/m^3",
|
|
19
|
+
"kilojoules": "kJ", "megajoules": "MJ",
|
|
20
|
+
"pascals": "Pa", "kilopascals": "kPa", "megapascals": "MPa", "gigapascals": "GPa",
|
|
21
|
+
"kelvin": "K", "degrees celsius": "degC",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
PACK = Pack(name="physics", version="0.1.0", claims=claims, priority=40, solve=controller.solve,
|
|
25
|
+
levels=controller.LEVELS, models=controller.MODELS, unit_words=UNIT_WORDS,
|
|
26
|
+
eval_set="evals/physics/problems.jsonl")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: plm-physics
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: plm pack: Physics and mechanical calculations, checked by units and sanity rules
|
|
5
|
+
Project-URL: Homepage, https://github.com/ro-rbl/plm-orchestrator
|
|
6
|
+
Project-URL: Source, https://github.com/ro-rbl/plm-orchestrator
|
|
7
|
+
Project-URL: Issues, https://github.com/ro-rbl/plm-orchestrator/issues
|
|
8
|
+
Author: ro-rbl
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.13
|
|
12
|
+
Requires-Dist: plm-core<0.2,>=0.1
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# plm-physics
|
|
16
|
+
|
|
17
|
+
Plm pack: Physics and mechanical calculations, checked by units and sanity rules.
|
|
18
|
+
|
|
19
|
+
A domain pack for [plm-core](https://github.com/ro-rbl/plm-orchestrator): install it with `plm packs install physics`, which also pulls the
|
|
20
|
+
models it needs into Ollama. Documentation, benchmark results and caveats: https://github.com/ro-rbl/plm-orchestrator
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
plm_physics/__init__.py,sha256=Us084AoBlEazUzqCHSOlHkPaI8I6NBVyryqJrTSY8N0,103
|
|
2
|
+
plm_physics/claims.py,sha256=hZiFNOjqW-7ztfxVv2minFxSrbAmh2-Y6VXFyF_6kjQ,2688
|
|
3
|
+
plm_physics/controller.py,sha256=bdJczQTft_rRHIeHQFK9HuKjvw-b1TpblzUk1DUc5mc,2042
|
|
4
|
+
plm_physics/levels.py,sha256=OIi5OFnS4VkemPrh-4CiN2boRUB9yKhn6nHMF6NWgY4,2380
|
|
5
|
+
plm_physics/manifest.py,sha256=MHLWMVkHsd3NwJhLiF7YFitJaxq6yByMAdrYaBVR4ps,1384
|
|
6
|
+
plm_physics-0.1.0.dist-info/METADATA,sha256=nlAC8K3ujqfoYNs_gbGIwqYyO6KWV8xEg_gypj8z3T4,862
|
|
7
|
+
plm_physics-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
8
|
+
plm_physics-0.1.0.dist-info/entry_points.txt,sha256=oYLov-o-POB-43Ix8Wz4jeOD7YV_H2qfOqO82WN3lfo,48
|
|
9
|
+
plm_physics-0.1.0.dist-info/licenses/LICENSE,sha256=KcrxZgcJO8eG0MOaibWc_3Pxvh6U_KruTKPaGwV6-vA,1063
|
|
10
|
+
plm_physics-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ro-rbl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|