Skip to content

Add autodiff building simulator benchmark #80665

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ set(SWIFT_BENCH_MODULES
single-source/ArraySetElement
single-source/ArraySubscript
single-source/AsyncTree
single-source/AutoDiffBuildingSimulator
single-source/BinaryFloatingPointConversionFromBinaryInteger
single-source/BinaryFloatingPointProperties
single-source/BitCount
Expand Down
253 changes: 253 additions & 0 deletions benchmark/single-source/AutoDiffBuildingSimulator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
//===--- AutoDiffBuildingSimulator.swift ----------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import TestsUtils
import _Differentiation

public let benchmarks = [
BenchmarkInfo(
name: "AutoDiffBuildingSimulator.Forward",
runFunction: run_AutoDiffBuildingSimulatorForward,
tags: [.validation, .api, .algorithm]),
BenchmarkInfo(
name: "AutoDiffBuildingSimulator.Reverse",
runFunction: run_AutoDiffBuildingSimulatorReverse,
tags: [.validation, .api, .algorithm]),

]

// Simulation parameters
let trials = 100
let timesteps = 20
let dTime: Float = 0.1
let printGradToCompare = false

// Definitions
let π = Float.pi

struct SimParams: Differentiable {
var tube: TubeType = .init()
var slab: SlabType = .init()
var quanta: QuantaType = .init()
var tank: TankType = .init()
var startingTemp: Float
}

struct TubeType: Differentiable {
var tubeSpacing: Float = 0.50292 // meters
var diameter: Float = 0.019 // m (3/4")
var thickness: Float = 0.001588 // m (1/16")
var resistivity: Float = 2.43 // (K/W)m
}

struct SlabType: Differentiable {
var temp: Float = 21.1111111 // °C
var area: Float = 100.0 // m^2
var Cp: Float = 0.2
var density: Float = 2242.58 // kg/m^3
var thickness: Float = 0.101 // m
}

struct QuantaType: Differentiable {
var power: Float = 0.0 // Watt
var temp: Float = 60.0 // °C
var flow: Float = 0.0006309 // m^3/sec
var density: Float = 1000.0 // kg/m^3
var Cp: Float = 4180.0 // ws/(kg • K)
}

struct TankType: Differentiable {
var temp: Float = 70.0
var volume: Float = 0.0757082
var Cp: Float = 4180.000
var density: Float = 1000.000
var mass: Float = 75.708
}

// Computations

@differentiable(reverse)
func computeResistance(floor: SlabType, tube: TubeType, quanta _: QuantaType) -> Float {
let geometry_coeff: Float = 10.0
// let f_coff = 0.3333333

let tubingSurfaceArea = (floor.area / tube.tubeSpacing) * π * tube.diameter
let resistance_abs = tube.resistivity * tube.thickness / tubingSurfaceArea

let resistance_corrected = resistance_abs * geometry_coeff // * (quanta.flow * f_coff)

return resistance_corrected
}

struct QuantaAndPower: Differentiable {
var quanta: QuantaType
var power: Float
}


extension Differentiable {
/// Applies the given closure to the derivative of `self`.
///
/// Returns `self` like an identity function. When the return value is used in
/// a context where it is differentiated with respect to, applies the given
/// closure to the derivative of the return value.
@inlinable
@differentiable(reverse, wrt: self)
func withDerivative(_: @escaping (inout TangentVector) -> Void) -> Self {
return self
}

@inlinable
@derivative(of: withDerivative)
func _vjpWithDerivative(
_ body: @escaping (inout TangentVector) -> Void
) -> (value: Self, pullback: (TangentVector) -> TangentVector) {
return (self, { grad in
var grad = grad
body(&grad)
return grad
})
}
}

@differentiable(reverse)
func computeLoadPower(floor: SlabType, tube: TubeType, quanta: QuantaType) -> QuantaAndPower {
let resistance_abs = computeResistance(floor: floor, tube: tube, quanta: quanta)

let conductance: Float = 1 / resistance_abs
let dTemp = floor.temp - quanta.temp
let power = dTemp * conductance

var updatedQuanta = quanta
updatedQuanta.power = power
let loadPower = -power

return QuantaAndPower(quanta: updatedQuanta, power: loadPower)
}

@differentiable(reverse)
func updateQuanta(quanta: QuantaType) -> QuantaType {
let workingVolume = (quanta.flow * dTime)
let workingMass = (workingVolume * quanta.density)
let workingEnergy = quanta.power * dTime
let TempRise = workingEnergy / quanta.Cp / workingMass
var updatedQuanta = quanta
updatedQuanta.temp = quanta.temp + TempRise

updatedQuanta.power = 0
return updatedQuanta
}

@differentiable(reverse)
func updateBuildingModel(power: Float, floor: SlabType) -> SlabType {
var updatedFloor = floor

let floorVolume = floor.area * floor.thickness
let floorMass = floorVolume * floor.density

updatedFloor.temp = floor.temp + ((power * dTime) / floor.Cp / floorMass)
return updatedFloor
}

struct TankAndQuanta: Differentiable {
var tank: TankType
var quanta: QuantaType
}

@differentiable(reverse)
func updateSourceTank(store: TankType, quanta: QuantaType) -> TankAndQuanta {
var updatedStore = store
var updatedQuanta = quanta

let massPerTime = quanta.flow * quanta.density
let dTemp = store.temp - quanta.temp
let power = dTemp * massPerTime * quanta.Cp

updatedQuanta.power = power

let tankMass = store.volume * store.density
let TempRise = (power * dTime) / store.Cp / tankMass
updatedStore.temp = store.temp + TempRise

return TankAndQuanta(tank: updatedStore, quanta: updatedQuanta)
}

var simParams = SimParams(startingTemp: 33.3)

@differentiable(reverse)
@inlinable public func absDifferentiable(_ value: Float) -> Float {
if value < 0 {
return -value
}
return value
}

func lossCalc(pred: Float, gt: Float) -> Float {
let diff = pred - gt
return absDifferentiable(diff)
}

// Simulations

@differentiable(reverse)
func simulate(simParams: SimParams) -> Float {
let pexTube = simParams.tube
var slab = simParams.slab
var tank = simParams.tank
var quanta = simParams.quanta

slab.temp = simParams.startingTemp
for _ in 0 ..< timesteps {
let tankAndQuanta = updateSourceTank(store: tank, quanta: quanta)
tank = tankAndQuanta.tank
quanta = tankAndQuanta.quanta

quanta = updateQuanta(quanta: quanta)

let quantaAndPower = computeLoadPower(floor: slab, tube: pexTube, quanta: quanta)
quanta = quantaAndPower.quanta
let powerToBuilding = quantaAndPower.power
quanta = updateQuanta(quanta: quanta)

slab = updateBuildingModel(power: powerToBuilding, floor: slab)
}
return slab.temp
}

var blackHole: Any?
@inline(never)
func dontLetTheCompilerOptimizeThisAway<T>(_ x: T) {
blackHole = x
}

@differentiable(reverse)
func fullPipe(simParams: SimParams) -> Float {
let pred = simulate(simParams: simParams)
let loss = lossCalc(pred: pred, gt: 27.344767)
return loss
}

@inline(never)
public func run_AutoDiffBuildingSimulatorForward(n: Int) {
for _ in 0 ..< n {
let forwardOnly = fullPipe(simParams: simParams)
dontLetTheCompilerOptimizeThisAway(forwardOnly)
}
}

@inline(never)
public func run_AutoDiffBuildingSimulatorReverse(n: Int) {
for _ in 0 ..< n {
let grad = gradient(at: simParams, of: fullPipe)
dontLetTheCompilerOptimizeThisAway(grad)
}
}
2 changes: 2 additions & 0 deletions benchmark/utils/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import ArrayRemoveAll
import ArraySetElement
import ArraySubscript
import AsyncTree
import AutoDiffBuildingSimulator
import BinaryFloatingPointConversionFromBinaryInteger
import BinaryFloatingPointProperties
import BitCount
Expand Down Expand Up @@ -229,6 +230,7 @@ register(ArrayRemoveAll.benchmarks)
register(ArraySetElement.benchmarks)
register(ArraySubscript.benchmarks)
register(AsyncTree.benchmarks)
register(AutoDiffBuildingSimulator.benchmarks)
register(BinaryFloatingPointConversionFromBinaryInteger.benchmarks)
register(BinaryFloatingPointProperties.benchmarks)
register(BitCount.benchmarks)
Expand Down