5.3. L2 — The pair is a system too
5.3.1. Synopsis
5.3.1.1. Purpose
Open each oscillator into its body and wall spring, add a coupling spring between bodies A and B, and treat the coupled pair as a system of its own sort, pairS. assemble sums a quantity of kind k over the parts of a whole of sort σ only if an Assembles σ k instance exists; the kind alone does not license the sum. The lesson provides Assembles instances for mass over a pair's bodies and for stiffness over springs in parallel. It provides none for angular frequency over a pair or for stiffness over springs in series, so those sums fail to type-check.
5.3.1.2. PKC vocabulary in scope
New at this step:
-
Composite(whole,part) — the object type of a whole and its parts. -
Decomposition(atom,union) — how a whole is carved into parts. -
Assembles— the aggregation licence, keyed by the sort of the whole and the kind. -
assemble— fold a licensed quantity over a decomposition. -
Extensive— a kind whose value over a union is the sum over its parts. -
DedicatedKind,KindOfProperty.dedicatedTo— a kind dedicated to one sort, such as the pair's normal-mode frequency. -
Component— a named component of a system's characterization. -
displacementK— spring extension; negative compressed, positive stretched.
Carried from earlier steps: IndividualQuantity, IndividualQuantity.add, DifferenceKind.ofScale, SortOfSystem, Sorted, Designated, massK, springConstantK.
5.3.1.3. Verifiable properties
-
assembleof the two bodies' masses gives the pair's mass as anIndividualQuantityabout the pair as a whole (type-checked)HCP.L2_ThePair#pair_mass. -
Adding the pair's mass to body A's mass fails with a type mismatch, because one is about the whole and the other about a part; adding pair P's mass to pair Q's mass fails the same way (refused)
HCP.L2_ThePair#refused_whole_part. -
assembleof angular frequency over the pair's bodies fails to type-check: there is noAssembles pairSinstance for angular frequency (refused)HCP.L2_ThePair#refused_frequency_assembly. -
The normal-mode frequency ω₊ is an angular-frequency kind dedicated to the sort
pairS, and it is provably different from any angular-frequency kind dedicated to a body or a spring (type-checked)HCP.L2_ThePair#normal_mode. -
assembleof stiffness over springs in parallel type-checks; over springs in series it fails: there is noAssembles seriesS springConstantKinstance (refused)HCP.L2_ThePair#parallel_stiffness,HCP.L2_ThePair#refused_series_stiffness. -
The parallel assembly equals the measured stiffness of the parallel combination, and the series law 1/k = 1/k₁ + 1/k₂ gives k = 2 for springs of stiffness 6 and 3 (theorem)
HCP.L2_ThePair#parallelLaw,HCP.L2_ThePair#licenseCashed,HCP.L2_ThePair#seriesLaw.
5.3.1.4. Errors prevented
-
Summing what does not sum: the normal-mode frequency taken as the sum of the bodies' angular frequencies.
assemblefails because noAssemblesinstance exists (HCP.L2_ThePair#refused_frequency_assembly). -
Wrong composition law: the stiffnesses of springs in series added as if the springs were in parallel (
HCP.L2_ThePair#refused_series_stiffness). -
Whole/part confusion: pair P's mass added to body A's mass, a quantity about the whole combined with a quantity about one of its parts (
HCP.L2_ThePair#refused_whole_part).
5.3.2. Persona
You are a PhysLib contributor: a physicist who writes Lean in PhysLib. You know Lean and
PhysLib and trust Dimension and WithDim; L0 and L1 have established the oscillator
context and the bearer-indexed quantities.
5.3.3. Pull
Once A and B are coupled, what bears the total mass and the oscillation frequency? Open the L1 black boxes into bodies A and B and springs kA, kC, and kB; kC is the new coupling spring. Each wall-spring oscillator supplies its body's mass and wall-spring stiffness, and the coupling spring makes the oscillators interact, so L1's no-interaction assumption falls. Mass is borne by the bodies and stiffness by the springs; the L1 oscillator-indexed ω² = k/m does not survive coupling. The normal-mode angular frequency belongs to the pair whole, which has its own sort. Following L1, one PairId name is one system: records under it are its states at different times, configurations, or rival estimates; comparing their shared-bearer quantities is the point. Different names denote distinct systems, so P and Q remain distinct even when their parameters match. Each spring has a signed extension, negative when compressed and positive when stretched; two configurations of pair P can compare their coupling-spring extensions with a typed displacement difference.
5.3.4. Risks at this step
-
Aggregation. In coupled pair P, the pair mass assembles from bodies A and B, but their normal-mode frequencies do not add; the mass assembly and frequency refusal are pinned at
HCP.L2_ThePair#pair_massandHCP.L2_ThePair#refused_frequency_assembly. -
Sort versus particular. The license to add body masses belongs to the coupled-pair sort, and parallel stiffness adds where series stiffness does not; the accepted parallel case and refused series case are pinned at
HCP.L2_ThePair#parallel_stiffnessandHCP.L2_ThePair#refused_series_stiffness.
Further reading
-
Exhibits/PointParticle. -
CaseStudies/PointParticle. -
Composite.AssembleswithExhibits/TwoRovers.
5.3.5. Small rigorous improvement
Question | Context change | Attribute change | Risk shown | PhysLib stage |
|---|---|---|---|---|
Once A and B are coupled, what bears the total mass and the oscillation frequency? | Open the L1 black boxes into bodies A and B and springs kA, kC, kB; kC joins A and B, so they interact. The coupled pair has its own sort and a nominal PairId independent of its parameters; spring configurations also carry signed extensions. | Per-part mass, stiffness, and displacement; total mass of the pair; normal-mode angular frequency of the pair; effective stiffness. | The pair's mass adds from its bodies, but normal-mode frequency and series stiffness do not; parallel stiffness adds under its own sort-specific license. | Stage 3: sort-specific assembly licenses. |
With B clamped, A is held by its wall spring kA and the coupling spring kC in parallel. The total mass belongs to the pair whole; angular frequency does not add across the bodies, and a normal mode is a property of the coupled whole. The parallel stiffness is extensive for the parallel sort, while series stiffness is whole-proper rather than additive. At L3, each body is still assumed rigid, homogeneous, and point-like, with its mass on the spring axis; an imbalance would make it wobble.
5.3.6. Learning path
# | Do |
Checked by | Concept disclosed |
|---|---|---|---|
1 | Open the boxes into bodies A/B and springs kA/kC/kB, give each part a nominal pair-indexed bearer, and compare compressed/stretched kC configurations of pair P. |
|
Mass belongs to a body, stiffness and signed extension belong to a spring; |
2 | Assemble both body masses into the pair whole. |
| The pair-specific whole mass is the sum of its parts' masses, read from the real fields. |
3 | Try to add the pair mass to body A's mass and to a pair with identical parameters but a different PairId. |
| Whole and part are distinct bearers; P and Q are different systems even when all parameter values and part names match. |
4 | Try to assemble the bodies' angular frequencies into the pair. |
|
Angular frequency has no |
5 | Give the normal mode its dedicated kind at the pair sort. |
| The normal mode is neither a body nor a spring property. |
6 | With B clamped, assemble kA and kC as springs in parallel. |
| The parallel whole has the sum of the two stiffness magnitudes. |
7 | Try to assemble kA and kB as two springs chained end to end. |
| No series-sort assembly license is registered. |
8 | Check the aggregation licenses on the exhibited pairs: parallel kA–kC and series kA–kB. |
| Parallel stiffness adds; series kA–kB gives 6 ∥ 3 = 2. Integer measurements are exact only for the exhibited pairs and do not model longer chains. |
Worksheet: HCP/Worksheets/L2_ThePair.lean · Solution: HCP/L2_ThePair.lean
5.3.7. Lesson
1. Open the box and represent configurations. Each body's upstream harmonic oscillator
supplies its wall-spring parameters; the coupling spring supplies stiffness, and each spring
has a signed extension. Two CoupledPair .P configurations admit a typed difference of
their kC extensions:
inductive Body | A | B
deriving DecidableEq, Repr
inductive Spring | kA | kC | kB
deriving DecidableEq, Repr
/--
One pair name is one system; its records are states, configurations, or rival estimates to
compare. Different names denote distinct systems.
-/
inductive PairId | P | Q
deriving DecidableEq, Repr
def bodyS : SortOfSystem := ⟨"point mass"⟩
def springS : SortOfSystem := ⟨"linear spring"⟩
def pairS : SortOfSystem := ⟨"coupled oscillator pair"⟩
def parallelS : SortOfSystem := ⟨"springs in parallel"⟩
def seriesS : SortOfSystem := ⟨"springs in series"⟩
instance : Sorted Body := ⟨fun _ => bodyS⟩
instance : Sorted Spring := ⟨fun _ => springS⟩
instance : Designated Body where
designation
| .A => ⟨"body A"⟩
| .B => ⟨"body B"⟩
designation_inj {x y} h := x:Bodyy:Bodyh:(match x with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ x = y
y:Bodyh:(match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.A = yy:Bodyh:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = y y:Bodyh:(match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.A = yy:Bodyh:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = y h:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = Body.Ah:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = Body.B h:(match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.A = Body.Ah:(match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.A = Body.Bh:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = Body.Ah:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = Body.B first | All goals completed! 🐙 | exact absurd (System.mk.inj h) (h:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ ¬"body B" = "body A" All goals completed! 🐙)
instance : Designated Spring where
designation
| .kA => ⟨"spring A"⟩
| .kC => ⟨"coupling spring"⟩
| .kB => ⟨"spring B"⟩
designation_inj {x y} h := x:Springy:Springh:(match x with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ x = y
y:Springh:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = yy:Springh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = yy:Springh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = y y:Springh:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = yy:Springh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = yy:Springh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = y h:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kAh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kCh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kB h:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = Spring.kAh:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = Spring.kCh:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = Spring.kBh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = Spring.kAh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = Spring.kCh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = Spring.kBh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kAh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kCh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kB first | All goals completed! 🐙 | exact absurd (System.mk.inj h) (h:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "coupling spring" }
| Spring.kB => { id := "spring B" }⊢ ¬"spring B" = "coupling spring" All goals completed! 🐙)
structure CoupledPair (π : PairId) where
osc : Body → ClassicalMechanics.HarmonicOscillator
kC : ℝ
extension : Spring → ℝ
structure PartOf (π : PairId) where
part : Body ⊕ Spring
deriving DecidableEq, Repr
def bodyPart (π : PairId) (b : Body) : PartOf π := ⟨Sum.inl b⟩
def springPart (π : PairId) (s : Spring) : PartOf π := ⟨Sum.inr s⟩
def springMagnitude {π : PairId} (p : CoupledPair π) : Spring → ℝ
| .kA => (p.osc .A).k
| .kC => p.kC
| .kB => (p.osc .B).k
def massOf {π : PairId} (p : CoupledPair π) (b : Body) :
IndividualQuantity
(Composite.part (σ := pairS) (bodyPart π b))
ForPhysLib.ClassicalMechanics.massK ℝ :=
⟨(p.osc b).m⟩
def stiffnessOf {π : PairId} (p : CoupledPair π) (s : Spring) :
IndividualQuantity
(Composite.part (σ := pairS) (springPart π s))
ForPhysLib.ClassicalMechanics.springConstantK ℝ :=
⟨springMagnitude p s⟩
def extensionOf {π : PairId} (p : CoupledPair π) (s : Spring) :
IndividualQuantity
(Composite.part (σ := pairS) (springPart π s))
ForPhysLib.ClassicalMechanics.displacementK ℝ :=
⟨p.extension s⟩
def couplingSpringExtensionDifference
(compressed stretched : CoupledPair .P) :
IndividualQuantity
(Composite.part (σ := pairS) (springPart .P .kC))
ForPhysLib.ClassicalMechanics.displacementK ℝ :=
IndividualQuantity.add DifferenceKind.ofScale
(extensionOf stretched .kC)
(-(extensionOf compressed .kC))
-- Same-name records: Constructs a displacementK IndividualQuantity about pair P's coupling spring as stretched minus compressed extension, comparing two states of one spring.
#guard_msgs in
example (compressed stretched : CoupledPair .P) :
IndividualQuantity
(Composite.part (σ := pairS) (springPart .P .kC))
ForPhysLib.ClassicalMechanics.displacementK ℝ :=
couplingSpringExtensionDifference compressed stretched
/-- Same-name records: Proves that pair P's coupling-spring displacement difference has magnitude stretched extension minus compressed extension. -/
theorem couplingSpringExtensionDifference_magnitude
(compressed stretched : CoupledPair .P) :
(couplingSpringExtensionDifference compressed stretched).magnitude =
stretched.extension .kC - compressed.extension .kC := compressed:CoupledPair PairId.Pstretched:CoupledPair PairId.P⊢ (couplingSpringExtensionDifference compressed stretched).magnitude =
stretched.extension Spring.kC - compressed.extension Spring.kC
compressed:CoupledPair PairId.Pstretched:CoupledPair PairId.P⊢ Carrier.add (stretched.extension Spring.kC) (-compressed.extension Spring.kC) =
stretched.extension Spring.kC - compressed.extension Spring.kC
compressed:CoupledPair PairId.Pstretched:CoupledPair PairId.P⊢ stretched.extension Spring.kC + -compressed.extension Spring.kC =
stretched.extension Spring.kC - compressed.extension Spring.kC
All goals completed! 🐙
Mass is now indexed by a body part and stiffness by a spring part. The oscillator-indexed
L1 ω² was the frequency of an isolated oscillator; it is not the normal-mode frequency
of this coupled pair.
2. Assemble the pair's mass. The two body masses are parts of Composite.whole; rfl
checks that the total reads the upstream oscillator fields:
instance : Assembles pairS ForPhysLib.ClassicalMechanics.massK :=
⟨DifferenceKind.ofScale⟩
def bodies : Decomposition Body := .union (.atom .A) (.atom .B)
def pairMass {π : PairId} (p : CoupledPair π) :
IndividualQuantity
(Composite.whole : Composite pairS (PartOf π))
ForPhysLib.ClassicalMechanics.massK ℝ :=
assemble (k := ForPhysLib.ClassicalMechanics.massK) pairS
(Composite.whole : Composite pairS (PartOf π))
(fun b : Body => Composite.part (σ := pairS) (bodyPart π b))
bodies
(fun b => massOf p b)
/-- Licensed composition: Proves that for pair π, the assembled whole-mass magnitude is body A's mass plus body B's mass. -/
theorem pairMass_magnitude {π : PairId} (p : CoupledPair π) :
(pairMass p).magnitude = (p.osc .A).m + (p.osc .B).m := π:PairIdp:CoupledPair π⊢ (pairMass p).magnitude = (p.osc Body.A).m + (p.osc Body.B).m
All goals completed! 🐙3. Refuse whole plus part, and one pair plus another. Total mass is meaningful, but its whole is not body A. A second pair is another system even when all parameters and part names match, because PairId is nominal:
-- Bearer safety: Refused: type mismatch between pair P's whole-mass bearer and body A's part-mass bearer; this prevents treating a part mass as the pair's whole mass.
/--
error: Application type mismatch: The argument
massOf p Body.A
has type
IndividualQuantity (Composite.part (bodyPart π Body.A)) ForPhysLib.ClassicalMechanics.massK ℝ
but is expected to have type
IndividualQuantity Composite.whole ForPhysLib.ClassicalMechanics.massK ℝ
in the application
IndividualQuantity.add ⋯ (pairMass p) (massOf p Body.A)
-/
#guard_msgs in
example {π : PairId} (p : CoupledPair π) :=
IndividualQuantity.add (k := ForPhysLib.ClassicalMechanics.massK)
DifferenceKind.ofScale (pairMass p) (massOf p .A)
-- Bearer safety: Refused: type mismatch between pair P's and pair Q's whole-mass bearers; this prevents combining masses from distinct pairs as one system.
/--
error: Application type mismatch: The argument
pairMass q
has type
IndividualQuantity (@Composite.whole pairS (PartOf PairId.Q)) ForPhysLib.ClassicalMechanics.massK ℝ
but is expected to have type
IndividualQuantity (@Composite.whole pairS (PartOf PairId.P)) ForPhysLib.ClassicalMechanics.massK ℝ
in the application
IndividualQuantity.add ⋯ (pairMass p) (pairMass q)
-/
#guard_msgs in
example (o : Body → ClassicalMechanics.HarmonicOscillator) :=
let p : CoupledPair .P := ⟨o, 2, fun _ => 0⟩
let q : CoupledPair .Q := ⟨o, 2, fun _ => 0⟩
IndividualQuantity.add (k := ForPhysLib.ClassicalMechanics.massK)
DifferenceKind.ofScale (pairMass p) (pairMass (π := .Q) q)4. Refuse to assemble angular frequency. The pair's license table has no entry for it:
-- Licensed composition: Refused: no Assembles pairS angularFrequency instance licenses deriving the pair's normal-mode frequency from the bodies' frequencies.
/--
error: failed to synthesize instance of type class
Assembles pairS ForPhysLib.ClassicalMechanics.Kinds.angularFrequency
Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
-/
#guard_msgs in
example :=
fun {π : PairId} (p : CoupledPair π) =>
fun (f : (b : Body) →
IndividualQuantity
(Composite.part (σ := pairS) (bodyPart π b))
ForPhysLib.ClassicalMechanics.Kinds.angularFrequency ℝ) =>
assemble (k := ForPhysLib.ClassicalMechanics.Kinds.angularFrequency) pairS
(Composite.whole : Composite pairS (PartOf π))
(fun b : Body => Composite.part (σ := pairS) (bodyPart π b))
bodies f
5. Give the normal mode its whole. A dedicated angular-frequency kind at pairS is
distinct from one dedicated to a body or a spring:
def whole : Component := ⟨"whole"⟩
def dkOmegaPlus : DedicatedKind :=
ForPhysLib.ClassicalMechanics.Kinds.angularFrequency.dedicatedTo
pairS ⟨"normal mode +"⟩
/-- Kind vs dimension: Proves that pair P's normal-mode kind differs from angular frequency dedicated to body A's whole; the mode belongs to the pair, not A alone. -/
theorem normal_mode_not_of_a_body :
dkOmegaPlus ≠
ForPhysLib.ClassicalMechanics.Kinds.angularFrequency.dedicatedFor Body.A whole :=
DedicatedKind.distinct_of_sort (⊢ dkOmegaPlus.sort ≠ (ForPhysLib.ClassicalMechanics.Kinds.angularFrequency.dedicatedFor Body.A whole).sort All goals completed! 🐙)
/-- Kind vs dimension: Proves that pair P's normal-mode kind differs from angular frequency dedicated to coupling spring kC's whole. -/
theorem normal_mode_not_of_a_spring :
dkOmegaPlus ≠
ForPhysLib.ClassicalMechanics.Kinds.angularFrequency.dedicatedFor Spring.kC whole :=
DedicatedKind.distinct_of_sort (⊢ dkOmegaPlus.sort ≠ (ForPhysLib.ClassicalMechanics.Kinds.angularFrequency.dedicatedFor Spring.kC whole).sort All goals completed! 🐙)6. Assemble parallel stiffness. With B clamped, A is held by kA and kC in parallel:
instance : Assembles parallelS ForPhysLib.ClassicalMechanics.springConstantK :=
⟨DifferenceKind.ofScale⟩
def parallelSprings : Decomposition Spring := .union (.atom .kA) (.atom .kC)
def stiffnessInSort {π : PairId} (p : CoupledPair π) (σ : SortOfSystem) (s : Spring) :
IndividualQuantity (Composite.part (σ := σ) (springPart π s))
ForPhysLib.ClassicalMechanics.springConstantK ℝ :=
⟨springMagnitude p s⟩
def parallelStiffness {π : PairId} (p : CoupledPair π) :
IndividualQuantity
(Composite.whole : Composite parallelS (PartOf π))
ForPhysLib.ClassicalMechanics.springConstantK ℝ :=
assemble (k := ForPhysLib.ClassicalMechanics.springConstantK) parallelS
(Composite.whole : Composite parallelS (PartOf π))
(fun s : Spring => Composite.part (σ := parallelS) (springPart π s))
parallelSprings
(fun s => stiffnessInSort p parallelS s)
/-- Licensed composition: Proves that pair π's parallelS whole has magnitude A.k + kC, the sum licensed for the parallel springs. -/
theorem parallelStiffness_magnitude {π : PairId} (p : CoupledPair π) :
(parallelStiffness p).magnitude = (p.osc .A).k + p.kC := π:PairIdp:CoupledPair π⊢ (parallelStiffness p).magnitude = (p.osc Body.A).k + p.kC
All goals completed! 🐙7. Refuse series stiffness. For kA and kB chained end to end, no additive series license is registered:
def seriesSprings : Decomposition Spring := .union (.atom .kA) (.atom .kB)
-- Licensed composition: Refused: no Assembles seriesS springConstantK instance; an additive rule would give kA + kB = 9, but the series measurement is 2.
/--
error: failed to synthesize instance of type class
Assembles seriesS ForPhysLib.ClassicalMechanics.springConstantK
Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
-/
#guard_msgs in
example {π : PairId} (p : CoupledPair π) :=
assemble (k := ForPhysLib.ClassicalMechanics.springConstantK) seriesS
(Composite.whole : Composite seriesS (PartOf π))
(fun s : Spring => Composite.part (σ := seriesS) (springPart π s))
seriesSprings
(fun s => stiffnessInSort p seriesS s)
8. Check the licenses against measurements. The measurements use HCP's own Spring.
PropertyValue.numeral is fixed at Int, so the exhibited kA–kB series chain is exact
(6 ∥ 3 = 2); these integer numerals do not model longer chains:
def stiffnessNumeral : Spring → Int
| .kA => 6
| .kC => 2
| .kB => 3
def parallelNumeral : Decomposition Spring → Int
| .atom s => stiffnessNumeral s
| .union a b => parallelNumeral a + parallelNumeral b
def parallelMeasurement : Measurement Spring := fun d =>
{ kind := ForPhysLib.ClassicalMechanics.springConstantK
numeral := parallelNumeral d
reference := "N/m" }
/-- Licensed composition: Proves that parallel springConstantK measurements add over parts, which is the law that licenses parallel stiffness assembly. -/
theorem parallel_extensive :
Extensive ForPhysLib.ClassicalMechanics.springConstantK parallelMeasurement :=
⟨fun _ => rfl, fun _ _ => rfl⟩def seriesNumeral : Decomposition Spring → Int
| .atom s => stiffnessNumeral s
| .union a b =>
(seriesNumeral a * seriesNumeral b) / (seriesNumeral a + seriesNumeral b)
def seriesMeasurement : Measurement Spring := fun d =>
{ kind := ForPhysLib.ClassicalMechanics.springConstantK
numeral := seriesNumeral d
reference := "N/m" }
/-- Composition law: Proves that series springs kA = 6 and kB = 3 measure 2, not their additive sum 9; series stiffness is not parallel addition. -/
theorem series_kA_kB_exact : (seriesMeasurement seriesSprings).numeral = 2 := ⊢ (seriesMeasurement seriesSprings).numeral = 2 All goals completed! 🐙
/-- Composition law: Proves that series measurement is WholeProper—neither additive nor uniform; kA = 6 and kB = 3 measure 2, not their sum 9. -/
theorem series_whole_proper :
WholeProper ForPhysLib.ClassicalMechanics.springConstantK seriesMeasurement where
ofKind := fun _ => rfl
notAdditive := ⟨.kA, .kB, ⊢ (seriesMeasurement ((Decomposition.atom Spring.kA).union (Decomposition.atom Spring.kB))).numeral ≠
(seriesMeasurement (Decomposition.atom Spring.kA)).numeral +
(seriesMeasurement (Decomposition.atom Spring.kB)).numeral All goals completed! 🐙⟩
notUniform := ⟨.kA, .kA, ⟨rfl, ⊢ (seriesMeasurement ((Decomposition.atom Spring.kA).union (Decomposition.atom Spring.kA))).numeral ≠
(seriesMeasurement (Decomposition.atom Spring.kA)).numeral All goals completed! 🐙⟩⟩
/-- Composition law: Proves that series springConstantK measurement is not extensive: kA = 6 and kB = 3 measure 2 rather than the additive 9, so no sum license applies. -/
theorem series_not_extensive :
¬ Extensive ForPhysLib.ClassicalMechanics.springConstantK seriesMeasurement :=
series_whole_proper.not_extensive/-- Licensed composition: Proves that parallel assembly of kA = 6 and kC = 2 for pair π has measured magnitude 8, as the extensive measurement requires. -/
theorem parallel_assembled_is_measured {π : PairId} (_p : CoupledPair π) :
(assemble (k := ForPhysLib.ClassicalMechanics.springConstantK) parallelS
(Composite.whole : Composite parallelS (PartOf π))
(fun s => Composite.part (σ := parallelS) (springPart π s)) parallelSprings
(fun s => ⟨(parallelMeasurement (.atom s)).numeral⟩)).magnitude
= (parallelMeasurement parallelSprings).numeral :=
assemble_eq_measured parallelS Composite.whole
(fun s => Composite.part (σ := parallelS) (springPart π s))
parallel_extensive parallelSprings
/-- Licensed composition: Proves that if seriesS springConstantK assembly is licensed, some decomposition's assembled reading differs from measurement; kA = 6 and kB = 3 give 9 versus 2. -/
theorem series_registered_would_lie
{π : PairId} (_p : CoupledPair π)
[Assembles seriesS ForPhysLib.ClassicalMechanics.springConstantK] :
∃ d : Decomposition Spring,
(assemble (k := ForPhysLib.ClassicalMechanics.springConstantK) (R := Int) seriesS
(Composite.whole : Composite seriesS (PartOf π))
(fun s => Composite.part (σ := seriesS) (springPart π s)) d
(fun s => ⟨(seriesMeasurement (.atom s)).numeral⟩)).magnitude
≠ (seriesMeasurement d).numeral :=
assemble_ne_measured seriesS Composite.whole
(fun s => Composite.part (σ := seriesS) (springPart π s)) series_whole_proper
The measurements verify the curation: parallel stiffness is extensive, while series
stiffness is whole-proper. Measurement carries an Int numeral, so the exhibited
kA–kB series value is exact (6 ∥ 3 = 2); these integer numerals do not model longer chains.
Registering the series sum would produce a magnitude that disagrees with its measurement.
The System-context card section of this chapter lists the bodies, all three springs,
the fixed walls, and the line of motion. The Lean model's nominal PartOf π index keeps
parts of differently identified pairs distinct even when their parameters match, while
body mass, spring stiffness, and the pair's normal-mode frequency stay on their respective
bearers. The on-axis, rigid, homogeneous, point-like body assumption returns at L3.
5.3.8. AI assistance contract
-
Use the HCP-owned
Body,Spring,PartOf, andCoupledPairtypes; do not import or reuse the Tutorial model. -
Keep body mass, spring stiffness, and pair normal-mode frequency on their respective bearers.
-
Give each spring a signed displacement-kind extension (negative when compressed, positive when stretched), and compare configurations under the same
PairId. -
Treat the pair as a whole distinct from every body and spring part.
-
State that the L1 no-interaction assumption falls here and that L3 revisits the on-axis, rigid-body assumption.
-
Use sort-specific
Assembleslicenses: mass for the pair and stiffness for parallel springs, but neither angular frequency for the pair nor stiffness for springs in series. -
Treat guarded elaboration errors as evidence only for the refusals they show.
5.3.9. Questions to ask the model so far
Question | Check | What the verdict means |
|---|---|---|
Where does total mass live? |
|
On |
Can compressed and stretched configurations of pair P be compared by kC extension? |
|
Yes; the typed displacement difference has magnitude |
Can a whole mass be added to one of its parts? |
| No; their composite object indices differ. |
Can two pairs' whole masses be added when their parameters are identical? |
|
No; nominal |
Does angular frequency assemble across the pair's bodies? |
| No; the pair sort has no angular-frequency license. |
What sort bears the normal mode? |
|
The dedicated kind is attached to |
What happens to stiffness when kA and kC act in parallel? |
| The effective magnitude is their sum, and the measurement is extensive. |
May the same summation rule be used for springs in series? |
|
No; kA–kB reads 6 ∥ 3 = 2, and a hypothetical sum license reports 9. The |
5.3.10. System-context card
Scope | System or component | Sort / status | L2 assumptions and attributes |
|---|---|---|---|
System | Coupled pair |
| Bodies A and B interact through kC. The normal-mode angular frequency belongs to the pair. |
Part | Body A |
| Homogeneous, rigid, point-like; mass mA lies on the spring axis. |
Part | Body B |
| Homogeneous, rigid, point-like; mass mB lies on the spring axis. |
Part | Wall spring kA |
| Linear spring from body A to the fixed wall; stiffness kA and signed extension (negative compressed, positive stretched). |
Part | Coupling spring kC |
| Linear spring joining bodies A and B; stiffness kC and signed extension (negative compressed, positive stretched). |
Part | Wall spring kB |
| Linear spring from body B to the fixed wall; stiffness kB and signed extension (negative compressed, positive stretched). |
Outside | Walls and line | Fixed, infinitely massive walls; one-dimensional motion | The wall springs remain attached to fixed boundaries and both bodies move along the line. |
The Lean model keeps each body's oscillator as the source of its mass and wall-spring
stiffness; kC is the pair's new coupling field, and extensionOf indexes each signed
displacement by its spring part. One PairId name is one system: records under it are states
at different times, configurations, or rival estimates to compare; distinct names are
distinct systems.
PartOf π uses nominal identity, so identical parameter records under P and Q still yield
different pair part types. The Measurement
numeral is an Int: the series example is exact for kA–kB (6 ∥ 3 = 2), but these integer
numerals do not model longer chains. L3 revisits the assumption that each body is rigid,
homogeneous, and point-like with its mass on the spring axis: if the mass is off-axis, the
body wobbles.