pcalc.presence

A Presence is a foundational concept in the presence calculus and this module includes all the concepts for modeling presence and the API to work with them.

Formally, a presence may represented as a 5-tuple:

$$ p = (e, b, t_0, t_1, m) $$

where:

  • $e \in E \subseteq D$ is an element (thing) in the domain $D$,
  • $b \in B \subseteq D$ is the boundary (place) in the domain $D$,
  • $[t_0, t_1)$ is the half-open interval of presence in time.
  • $m \in R$ is called the mass of the presence.

A presence can be viewed as a function:

$$ P: E \times B \times \overline{\mathbb{R}} \times \overline{\mathbb{R}} \to \mathbb{R} $$

where

  • $P(e, b, t_0, t_1) = m \gt 0 $  if $e$ is present in $b$ during $[t_0, t_1)$
  • $P(e, b, t_0, t_1) = 0 $    otherwise

When $m \in \{ 0,1 \} $ we get the simplest form of presence: a binary assertion. This is a statement that an element was present in a boundary for a continuous time interval (with mass = 1).

Presence mass is core concept that will be described in more detail shortly - for now, it is sufficient to think of it as a value or weight attached to a presence assertion that is derived from some function on the underlying domain that must be "measurable" in a precise mathematical sense.

The key thing is that the machinery of the presence calculus lets you work identically with binaru presences which are much more intutive and easy to understand, and presences derived on arbitrary measures. This generality gives us a great deal of power on reasoning about logical, desterministic/stochastic, linear/non-linear and complex adaptive systems using exactly the same set of tools.

Presence onset and reset

$t_0$ is called the onset time of the presence—the instant at which the presence transitions from zero to non-zero mass. $t_1$ is called the reset time — the instant at which the presence transitions back to zero.

We will say the presence is active over the half-open interval $[t_0, t_1)$ (meaning it includes $t_0$ but excludes $t_1$).

Figure: A presence interval $[t_0, t_1)$ with $t_0$ included (●) and $t_1$ excluded (○).

This diagram illustrates a presence assertion over a half-open interval $[t_0, t_1)$, where element $e$ is continuously present in boundary $b$ starting at $t_0$ (included) and ending just before $t_1$ (excluded).

The Open World Assumption

The time interval $[t_0, t_1)$ is defined over the extended reals $\overline{\mathbb{R}}$: the real line $\mathbb{R}$ extended with the symbols $-\infty$ and $+\infty$.

This reflects the presence calculus's open world assumption, which permits intervals that extend indefinitely into the past or future. A presence with $t_0 = -\infty$ represents a presence whose beginning is unknown, and $t_1 = +\infty$ represents a presence whose end is unknown.

Presences with both start and end unknown are valid and represent eternal presences. Many of the most interesting questions in the presence calculus involve reasoning about the dynamics of a domain under the epistemic uncertainty introduced by such presences.


Presence Assertions

A Presence Assertion is an assertion about the existence of a presence from the standpoint of a specific observer at a specific time.

A presence assertion is thus a seven-tuple $(e, b, t_0, t_1, m, o, t_2)$ where:

  • $(e, b, t_0, t_1, m)$ is the presence being asserted,
  • $o \in O \subseteq D$ is the observer (a domain entity), and
  • $t_2 \in \overline{\mathbb{R}}$ is the assertion time.

Both observer and assertion time may themselves be unknown, and together they reflect the epistemic provenance of the assertion.

Given our open world assumptions, we assume by default that we are reasoning about dynamic sets of presences that reflect observations by various observers on the underlying domain. Presence assertions may be added or removed from this set, which in turn changes what we can say about the state of the domain. We assume (by construction, if necessary) that observers are part of the domain $D$.

For example, learning the reset time of a presence—when it was previously unknown—allows us to replace the old assertion with a new one that reflects greater epistemic certainty about the domain state. A new assertion may update the onset or reset time based on new information from different observers (e.g., sensor disagreements), reflecting the evolving nature of knowledge in an open world.

At any point in time, we are reasoning about a known subset of the domain's history, and potentially even making assertions about the future (estimates, forecasts, etc., which can also be modeled as presence assertions), each with its own epistemic status.

It is crucial to note that the presence calculus treats presence assertions as axiomatic: it does not question or infer their validity, nor does it make decisions based on provenance. However, the provenance of assertions may be used to reason about the reliability or implications of the conclusions reached via the machinery of the calculus.

For example, suppose in a traffic domain we observe that a vehicle (the element) crossed the 1-mile marker at 11:00 AM and the 2-mile marker at 11:10 AM. If we assert that the vehicle was continuously present (i.e., within the boundary between those markers) during the interval [11:00, 11:11), that assertion depends on domain-level assumptions—such as continuity of motion, absence of detours, or the nonexistence of teleportation.

In a different domain—for example, one in which a wormhole allows instantaneous travel between Mars and mile 1.6—such an assertion might be invalid.

The presence calculus does not attempt to resolve or validate such assumptions. It simply assumes that all presence assertions are logically valid according to domain semantics and proceeds to compute their consequences using the machinery of the calculus.

The key requirement though, is that the presence mass must be well defined. This brings us to the concept of a domain Signal over which presences are defined and asserted.

Domain Signals and Presence Mass

A Domain Signal is any function that varies over time and is associated with elements and boundaries in a given domain $D$. It may represent activity, value, demand, or any other observable quantity relevant to the system.

Let $$ F : (e, b, t) \to \mathbb{R}_{\geq 0} $$ be a function that assigns a non-negative value to each element–boundary–time triple. This is the general form of a domain signal in the Presence Calculus.

For the signal to be meaningful in the Presence Calculus, it must be measurable - that is, it must define a quantity that can be accumulated over time.

Measure theory and the Lebesgue measure provide the standard mathematical language to model such functions. In particular, the Lebesgue integral of the signal defines the presence mass: the total contribution it makes over a continuous time interval.

Integrability is the core requirement here—if a signal is Lebesgue integrable over an interval $[t_0, t_1)$, then its presence mass is well-defined and finite. This makes it possible to work with irregular, discontinuous, or sparse signals while still reasoning rigorously about accumulation and duration.

Given $(e, b, t_0, t_1)$, the presence mass is given by: $$ \mu_{e,b} = \int_{t_0}^{t_1} F(e, b, t)\, dt $$

This integral defines how much presence is accumulated over the interval for the given element–boundary pair, combining both the values of the function and how they are distributed over time.

This is not a particularly restrictive condition: the class of Lebesgue integrable functions is broad and includes most real-world signals encountered in practice. However, insisting that we use the Lebesgue integral to define mass over an interval (as opposed to, say, a statistical average) has meaningful consequences when modeling discontinuous signals. It ensures that the resulting "mass" retains its intended semantics as a presence measure, even as we apply more complex algebraic and topological operations to sets of presence assertions.

The Presence Calculus builds on this foundation, treating presence assertions as time-localized signals whose mass can be integrated, compared, and composed across boundaries.

In addition to integrability, a key property of a Lebesgue measure is that it must be finitely additive—that is, we must be able to compute sums over unions and intersections of sets of intervals.

Time as Topology

This brings us to a fundamental property of presences—and the key distinction between presences and point-in-time events: the continuity of presence gives us a natural and mathematically precise way to define a topology of presence over the extended real line $\overline{\mathbb{R}}$.

Each presence defines a basic open set in this topology, corresponding to its half-open interval $[t_0, t_1) \subset \overline{\mathbb{R}}.$ The collection of all such intervals forms a basis for a topology over time, using the standard technique from point-set topology of generating a topology from a basis of open sets. See BasisTopology for details.

This topological structure plays an important role in ensuring that presence mass is well-behaved under composition: it allows us to combine, intersect, and partition presences in ways that naturally satisfy the finite additivity conditions required for presence mass to behave like a Lebesgue measure.

This means that presence assertions allow us to reason about concepts such as nearness, overlap, continuity, and structure of presence in time— that is, how domain entities relate across time with mathematical precision.

It also allows us to compute sums, rates, averages, and other quantities across presences with well-defined semantics. This may seem like a technical detail, but it is essential: it allows us to handle sparse, discontinuous, bursty, or qualitative data with the same rigor as continuous signals.

The consequences of reasoning with presence assertions are temporal and topological: they give rise to notions of proximity, flow, and co-presence in time that reveal the shape, connectedness, and dynamics of the domain in mathematically precise ways.

With presence assertions, we can reason about patterns such as:

  • elements that tend to be present together (co-presence),
  • transitions between boundaries,
  • regions of concentrated or evolving presence, and
  • causal relationships between presences.

Furthermore, the Presence Calculus introduces certain topological invariants (see PresenceInvariant) that express general laws of conservation of presence mass.

This provides the foundational structure for reasoning about the dynamics and causality of presence in a domain.


Presence API

This module defines the data structure and associated methods for Presence, and serves as the canonical representation of presence assertions and their topological primitives in the presence calculus

Note: When interfacing with external systems that operate in wallclock time you will need to convert between timestamps and dates and floating point numbers when constructing presence assertions.

The TimeModel class (pcalc.time_model.TimeModel) is provided for this purpose.


  1# -*- coding: utf-8 -*-
  2# Copyright (c) 2025 Krishna Kumar
  3# SPDX-License-Identifier: MIT
  4"""
  5A *Presence* is a foundational concept in the presence calculus and this module
  6includes all the concepts for modeling presence and the API to work with them.
  7
  8Formally, a presence may represented as a 5-tuple:
  9
 10$$
 11p = (e, b, t_0, t_1, m)
 12$$
 13
 14where:
 15- $e \in E \subseteq D$ is an element (thing) in the domain $D$,
 16- $b \in B \subseteq D$ is the boundary (place) in the domain $D$,
 17- $[t_0, t_1)$ is the half-open interval of presence in time.
 18- $m \in R$ is called the
 19<em>mass</em> of the presence.
 20
 21A presence can be viewed as a function:
 22
 23$$
 24P: E \\times B \\times \\overline{\\mathbb{R}} \\times \\overline{\\mathbb{R}} \\to \\mathbb{R}
 25$$
 26
 27
 28where
 29
 30
 31- $P(e, b, t_0, t_1) = m \gt 0 $  if $e$ is present in $b$ during $[t_0, t_1)$
 32- $P(e, b, t_0, t_1) = 0 $    otherwise
 33
 34
 35When $m \in
 36\\\\{ 0,1 \\\\} $ we get the simplest form of presence: a binary assertion. This is a statement that an element was
 37present in a boundary for a continuous time interval (with mass = 1). 
 38
 39Presence *mass* is core concept that
 40will be described in more detail shortly - for now, it is sufficient to think of it as a value or weight attached to
 41a presence assertion that is derived from some function on the underlying domain that must be "measurable" in a precise
 42mathematical sense.
 43
 44The key thing is that the machinery of the presence calculus lets you work identically with binaru presences which are much more
 45intutive and easy to understand, and presences derived on arbitrary measures. This generality gives us a great deal of power
 46on reasoning about logical, desterministic/stochastic, linear/non-linear and complex adaptive systems using exactly the
 47same set of tools.
 48
 49### Presence onset and reset
 50
 51$t_0$ is called the <em>onset time</em> of the presence—the instant at which the
 52presence transitions from zero to non-zero mass. $t_1$ is called the *reset time* —
 53the instant at which the presence transitions back to zero.
 54
 55We will say the presence is *active*
 56over the half-open interval $[t_0, t_1)$ (meaning it
 57includes $t_0$ but excludes $t_1$).
 58
 59<div style="text-align: center; margin: 2em 0;">
 60  <img src="../../assets/pcalc/half_open_presence.png" width="400px" />
 61  <div style="font-size: 0.9em; color: #555; margin-top: 0.5em;">
 62    Figure: A presence interval $[t_0, t_1)$ with $t_0$ included (●) and
 63    $t_1$ excluded (○).
 64  </div>
 65</div>
 66
 67This diagram illustrates a presence assertion over a half-open interval
 68$[t_0, t_1)$, where element $e$ is continuously present in boundary $b$
 69starting at $t_0$ (included) and ending just before $t_1$ (excluded).
 70
 71
 72
 73### The Open World Assumption
 74
 75The time interval $[t_0, t_1)$ is defined over the <em>extended</em> reals
 76$\overline{\mathbb{R}}$: the real line $\mathbb{R}$ extended with the symbols
 77$-\infty$ and $+\infty$.
 78
 79This reflects the presence calculus's open world assumption, which permits
 80intervals that extend indefinitely into the past or future. A presence with
 81$t_0 = -\infty$ represents a presence whose beginning is unknown, and
 82$t_1 = +\infty$ represents a presence whose end is unknown.
 83
 84Presences with both start and end unknown are valid and represent eternal
 85presences. Many of the most interesting questions in the presence calculus
 86involve reasoning about the dynamics of a domain under the epistemic
 87uncertainty introduced by such presences.
 88
 89---
 90### Presence Assertions
 91
 92A Presence Assertion is an assertion about the existence of a presence from
 93the standpoint of a specific observer at a specific time.
 94
 95A presence assertion is thus a seven-tuple
 96$(e, b, t_0, t_1, m, o, t_2)$ where:
 97
 98- $(e, b, t_0, t_1, m)$ is the presence being asserted,
 99- $o \in O \subseteq D$ is the observer (a domain entity), and
100- $t_2 \in \overline{\mathbb{R}}$ is the assertion time.
101
102Both observer and assertion time may themselves be unknown, and together
103they reflect the epistemic *provenance* of the assertion.
104
105Given our open world assumptions, we assume by default that we are reasoning
106about *dynamic* sets of presences that reflect observations by various observers
107on the underlying domain. Presence assertions may be added or removed
108from this set, which in turn changes what we can say about the
109state of the domain. We assume (by construction, if necessary) that observers
110are part of the domain $D$.
111
112For example, learning the reset time of a presence—when it was previously
113unknown—allows us to replace the old assertion with a new one that reflects
114greater epistemic certainty about the domain state. A new assertion may update
115the onset or reset time based on new information from different observers
116(e.g., sensor disagreements), reflecting the evolving nature of knowledge in
117an open world.
118
119At any point in time, we are reasoning about a known subset of the domain's
120history, and potentially even making assertions about the *future*
121(estimates, forecasts, etc., which can also be modeled as presence assertions),
122each with its own epistemic status.
123
124It is crucial to note that the presence calculus treats presence assertions as
125*axiomatic*: it does not question or infer their validity, nor does it make
126decisions based on provenance. However, the provenance of assertions may be
127used to reason about the reliability or implications of the conclusions reached
128via the machinery of the calculus.
129
130For example, suppose in a traffic domain we observe that a vehicle (the
131element) crossed the 1-mile marker at 11:00 AM and the 2-mile marker at
13211:10 AM. If we assert that the vehicle was continuously present (i.e.,
133within the boundary between those markers) during the interval [11:00, 11:11),
134that assertion depends on domain-level assumptions—such as continuity of
135motion, absence of detours, or the nonexistence of teleportation.
136
137In a different domain—for example, one in which a wormhole allows
138instantaneous travel between Mars and mile 1.6—such an assertion might be
139invalid.
140
141The presence calculus does *not* attempt to resolve or validate such
142assumptions. It simply assumes that all presence assertions are logically
143valid according to domain semantics and proceeds to compute their
144consequences using the machinery of the calculus.
145
146The key requirement though, is that the *presence mass* must be well defined.
147This brings us to the concept of a domain Signal over which presences are defined
148and asserted.
149
150### Domain Signals and Presence Mass
151
152A Domain Signal is any function that varies over time and is associated with
153elements and boundaries in a given domain $D$. It may represent activity,
154value, demand, or any other observable quantity relevant to the system.
155
156Let
157$$
158F : (e, b, t) \\to \\mathbb{R}_{\\geq 0}
159$$
160be a function that assigns a non-negative value to each element–boundary–time triple.
161This is the general form of a domain signal in the Presence Calculus.
162
163For the signal to be meaningful in the Presence Calculus, it must be *measurable* -
164that is, it must define a quantity that can be accumulated over time.
165
166*Measure theory* and the *Lebesgue measure* provide the standard mathematical language
167to model such functions. In particular, the *Lebesgue integral* of the signal
168defines the presence mass: the total contribution it makes over
169a continuous time interval.
170
171Integrability is the core requirement here—if a
172signal is Lebesgue integrable over an interval $[t_0, t_1)$, then its
173presence mass is well-defined and finite. This makes it possible to work with
174irregular, discontinuous, or sparse signals while still reasoning rigorously
175about accumulation and duration.
176
177Given $(e, b, t_0, t_1)$, the presence mass is given by:
178$$
179\\mu_{e,b} = \\int_{t_0}^{t_1} F(e, b, t)\\, dt
180$$
181
182This integral defines how much presence is accumulated over the interval for
183the given element–boundary pair, combining both the values of the function
184and how they are distributed over time.
185
186This is not a particularly restrictive condition: the class of Lebesgue integrable
187functions is broad and includes most real-world signals encountered in practice.
188However, insisting that we use the Lebesgue integral to define mass over an interval
189(as opposed to, say, a statistical average) has meaningful consequences when modeling
190discontinuous signals. It ensures that the resulting "mass" retains its intended semantics
191as a presence measure, even as we apply more complex algebraic and topological operations
192to sets of presence assertions.
193
194The Presence Calculus builds on this foundation, treating presence assertions
195as time-localized signals whose mass can be integrated, compared, and composed
196across boundaries.
197
198In addition to integrability, a key property of a Lebesgue measure is that it
199must be finitely additive—that is, we must be able to compute sums over
200unions and intersections of sets of intervals.
201
202### Time as Topology
203
204This brings us to a fundamental property of presences—and the key distinction
205between presences and point-in-time events: the continuity of presence gives us
206a natural and mathematically precise way to define a *topology* of presence
207over the extended real line $\overline{\\mathbb{R}}$.
208
209Each presence defines a basic open set in this topology, corresponding to its
210half-open interval $[t_0, t_1) \\subset \\overline{\\mathbb{R}}.$ The collection of
211all such intervals forms a basis for a topology over time, using the standard
212technique from point-set topology of generating a topology from a basis of
213open sets. See [BasisTopology](./basis_topology.html) for details.
214
215This topological structure plays an
216important role in ensuring that presence mass is well-behaved under composition:
217it allows us to combine, intersect, and partition presences in ways that
218naturally satisfy the finite additivity conditions required for presence mass
219to behave like a Lebesgue measure.
220
221This means that presence assertions allow us to reason about concepts such as
222*nearness*, *overlap*, *continuity*, and *structure* of presence in time—
223that is, how domain entities relate across time with mathematical precision.
224
225It also allows us to compute sums, rates, averages, and other quantities across
226presences with well-defined semantics. This may seem like a technical detail,
227but it is essential: it allows us to handle sparse, discontinuous, bursty, or
228qualitative data with the same rigor as continuous signals.
229
230The consequences of reasoning with presence assertions are *temporal* and *topological*:
231they give rise to notions of proximity, flow, and co-presence in time that reveal
232the shape, connectedness, and dynamics of the domain in mathematically precise ways.
233
234With presence assertions, we can reason about patterns such as:
235- elements that tend to be present together (*co-presence*),
236- transitions between boundaries,
237- regions of concentrated or evolving presence, and
238- causal relationships between presences.
239
240Furthermore, the Presence Calculus introduces certain topological invariants
241(see [PresenceInvariant](./presence_invariant_discrete.html)) that express
242general *laws of conservation of presence mass*.
243
244This provides the foundational structure for reasoning about the *dynamics*
245and *causality* of presence in a domain.
246
247---
248
249## Presence API
250
251This module defines the data structure and associated methods for Presence, 
252and serves as the canonical representation of presence assertions and their topological
253 primitives in the
254presence calculus
255
256Note: When interfacing with external systems that operate in wallclock time you will need to
257convert between timestamps and dates and floating point numbers when constructing presence assertions.
258
259The [TimeModel](./time_model.html) class (`pcalc.time_model.TimeModel`) is provided for this purpose.
260
261---
262"""
263
264from __future__ import annotations
265from typing import Optional, Protocol, runtime_checkable
266
267from dataclasses import dataclass
268from typing import Optional, Callable
269
270from .entity import EntityProtocol
271
272
273PresenceFunction = Callable[[EntityProtocol, EntityProtocol, float], float]
274# Presence functions and Presence Protocols
275"""
276A `PresenceFunction` $f$ is a time-varying function represented as a callable of the form:
277
278```python
279(e: EntityProtocol, b: EntityProtocol, t: float) -> float
280```
281
282It returns a real-valued signal that is non-zero only when $t$ falls within some half open 
283interval $[t_s, t_e)$
284
285Presence functions must satisfy the following property:
286
287- For some half-open interval $[t_s, t_e) in \overline{\mathbb{R}}$, the function is non-zero only within that interval:
288
289    - $f(e, b, t) \\\\ne 0$ only if $t \in [t_s, t_e)$
290    - $f(e, b, t) = 0$ elsewhere
291
292The returned value for $f$ in $\mathbb{R}$ can represent any time-varying quantity that has meaning in the domain—
293such as revenue, attention, sensor signal strength, or value accrual. Note that since the function domain is over
294the extended reals, we do allow these presences to extend infinitely into the past and the future. 
295
296For example, in a sales domain, if the element `e` is a customer and `b` is a customer segment,
297the presence function might represent revenue from that customer at time `t`. If $[t_s, t_e)$
298represents the customer lifetime, then customer revenue is zero outside that interval. 
299
300<div style="text-align: center; margin:2em">
301  <img src="../../assets/pcalc/presence_function.png" width="600px" />
302  <div style="font-size: 0.9em; color: #555; margin-top: 1em; margin-bottom: 1em;">
303    Figure 1: A presence function for customer revenues in the sales domain. 
304  </div>
305</div>
306
307Note that Inside the interval, the value may still drop to zero — presence does not
308require that every value for the presence function remain greater than zero during the interval.
309
310
311Here are more examples of presence functions in python. 
312Note that `constant_presence` corresponds to the most commonly assumed form of
313presence in classical models. It represents entities with uniform presence
314over a finite interval—from the start of the event to its end—with constant
315instantaneous mass.
316
317Much of queueing theory and flow analysis is based solely on this simple
318model, where presence is modeled as a rectangular pulse with finite duration
319and constant density.
320
321The presence calculus generalizes this by allowing arbitrary real-valued
322functions with compact support over the extended real line
323$\\overline{\\mathbb{R}}$, enabling richer modeling of non-uniform, delayed,
324forecasted, or probabilistic presences.
325
326
327```python
328# Boolean presence: returns 1.0 within [start, end), 0.0 elsewhere
329def constant_presence(start: float, end: float) -> PresenceFunction:
330    def f(e, b, t):
331        return 1.0 if start <= t < end else 0.0
332    return f
333
334# Ramped presence: increases linearly from 0.0 to 1.0 over [start, end)
335def ramp_presence(start: float, end: float) -> PresenceFunction:
336    def f(e, b, t):
337        if start <= t < end:
338            return (t - start) / (end - start)
339        return 0.0
340    return f
341
342# Delayed pulse: returns fixed value only after a delay from start
343def delayed_presence(start: float, end: float, delay: float = 1.0) -> PresenceFunction:
344    def f(e, b, t):
345        if start + delay <= t < end:
346            return 1.0
347        return 0.0
348    return f
349```
350
351These functions form the foundation of presence semantics.
352
353Presence functions are defined *in the domain* and serve as the bridge between discrete
354domain events and continuous presence intervals used in the presence calculus. 
355They are exposed to the presence calculus via the `PresenceProtocol`.
356
357"""
358
359
360@runtime_checkable
361class PresenceProtocol(Protocol):
362    """
363    The PresenceProtocol represents the  contract between an arbitrary presence function
364    and the foo
365    """
366
367    def __call__(self, t0: float, t1: float) -> float: ...
368
369
370
371    def overlaps(self, t0: float, t1: float) -> bool: ...
372
373    @property
374    def onset_time(self) -> float: ...
375
376    @property
377    def reset_time(self) -> float: ...
378
379
380@dataclass(frozen=True)
381class PresenceAssertion:
382    """
383
384    This is the fundamental, immutable construct of The presence calculus,
385    and asserts the presence of an element at a boundary over a continuous interval of time.
386
387    A presence is defined over a half-open interval $[t_0, t_1)$ on the real
388    line, where $t_0$ is the onset time and $t_1$ is the reset time.
389
390    The
391    following constraints must hold:
392
393    - $t_0 < t_1$
394    - $t_0 \\in \\mathbb{R} \\cup \\{-\\infty\\}$
395    - $t_1 \\in \\mathbb{R} \\cup \\{+\\infty\\}$
396    - $t_0 \\ne +\\infty$
397    - $t_1 \\ne -\\infty$
398
399    These rules ensure that the interval is well-formed, bounded on the left,
400    and open on the right. Intervals such as $[2.0, 5.0)$, $[-\\infty, 4.2)$,
401    and $[1.0, +\\infty)$ are allowed, as are the special case $[-\\infty,
402    +\\infty)$ and $[0,0),$ the empty presence.
403
404    With the empty presence as the only exception, intervals with zero or negative duration, or with reversed or
405    undefined bounds, are disallowed.
406    """
407    element: Optional[EntityProtocol]
408    boundary: Optional[EntityProtocol]
409    onset_time: float
410    reset_time: float
411    presence: Optional[PresenceProtocol] = None
412    observer: Optional[EntityProtocol] | str = "observed"
413    assert_time: Optional[float] = 0.0
414
415    def __post_init__(self):
416        """
417        Validates the temporal bounds of the presence interval
418        """
419        if self.onset_time >= self.reset_time:
420            if not (self.onset_time == self.reset_time == 0):
421                raise ValueError(
422                    f"Invalid interval: onset_time ({self.onset_time}) must be less than reset_time ({self.reset_time})")
423
424        if self.onset_time == float("inf"):
425            raise ValueError("Presence cannot begin at +inf")
426
427        if self.reset_time == float("-inf"):
428            raise ValueError("Presence cannot end at -inf")
429
430    def overlaps(self, t0: float, t1: float) -> bool:
431        return self.reset_time > t0 and self.onset_time < t1
432
433    def mass(self) -> float:
434        """
435        Returns the mass of the presence.
436
437
438        
439        """
440        onset = max(0.0, self.onset_time)
441        return max(0.0, self.reset_time - onset)
442
443    def mass_contribution(self, t0: float, t1: float) -> float:
444        if t0 >= t1 or not self.overlaps(t0, t1):
445            return 0.0
446        start = max(self.onset_time, t0)
447        end = min(self.reset_time, t1)
448        return max(0.0, end - start)
449
450    def __str__(self) -> str:
451        element_str = str(self.element) if self.element is not None else "None"
452        boundary_str = str(self.boundary) if self.boundary is not None else "None"
453        interval_str = f"[{self.onset_time}, {self.reset_time})"
454        return (
455            f"Presence(element={element_str}, boundary={boundary_str}, "
456            f"interval={interval_str}, provenance={self.observer})"
457        )
458
459
460EMPTY_PRESENCE: PresenceAssertion = PresenceAssertion(
461    element=None,
462    boundary=None,
463    presence=None,
464    onset_time=0.0,
465    reset_time=0.0,
466    observer="empty",
467)
PresenceFunction = typing.Callable[[pcalc.EntityProtocol, pcalc.EntityProtocol, float], float]

A PresenceFunction $f$ is a time-varying function represented as a callable of the form:

(e: EntityProtocol, b: EntityProtocol, t: float) -> float

It returns a real-valued signal that is non-zero only when $t$ falls within some half open interval $[t_s, t_e)$

Presence functions must satisfy the following property:

  • For some half-open interval $[t_s, t_e) in \overline{\mathbb{R}}$, the function is non-zero only within that interval:

    • $f(e, b, t) \ne 0$ only if $t \in [t_s, t_e)$
    • $f(e, b, t) = 0$ elsewhere

The returned value for $f$ in $\mathbb{R}$ can represent any time-varying quantity that has meaning in the domain— such as revenue, attention, sensor signal strength, or value accrual. Note that since the function domain is over the extended reals, we do allow these presences to extend infinitely into the past and the future.

For example, in a sales domain, if the element e is a customer and b is a customer segment, the presence function might represent revenue from that customer at time t. If $[t_s, t_e)$ represents the customer lifetime, then customer revenue is zero outside that interval.

Figure 1: A presence function for customer revenues in the sales domain.

Note that Inside the interval, the value may still drop to zero — presence does not require that every value for the presence function remain greater than zero during the interval.

Here are more examples of presence functions in python. Note that constant_presence corresponds to the most commonly assumed form of presence in classical models. It represents entities with uniform presence over a finite interval—from the start of the event to its end—with constant instantaneous mass.

Much of queueing theory and flow analysis is based solely on this simple model, where presence is modeled as a rectangular pulse with finite duration and constant density.

The presence calculus generalizes this by allowing arbitrary real-valued functions with compact support over the extended real line $\overline{\mathbb{R}}$, enabling richer modeling of non-uniform, delayed, forecasted, or probabilistic presences.

# Boolean presence: returns 1.0 within [start, end), 0.0 elsewhere
def constant_presence(start: float, end: float) -> PresenceFunction:
    def f(e, b, t):
        return 1.0 if start <= t < end else 0.0
    return f

# Ramped presence: increases linearly from 0.0 to 1.0 over [start, end)
def ramp_presence(start: float, end: float) -> PresenceFunction:
    def f(e, b, t):
        if start <= t < end:
            return (t - start) / (end - start)
        return 0.0
    return f

# Delayed pulse: returns fixed value only after a delay from start
def delayed_presence(start: float, end: float, delay: float = 1.0) -> PresenceFunction:
    def f(e, b, t):
        if start + delay <= t < end:
            return 1.0
        return 0.0
    return f

These functions form the foundation of presence semantics.

Presence functions are defined in the domain and serve as the bridge between discrete domain events and continuous presence intervals used in the presence calculus. They are exposed to the presence calculus via the PresenceProtocol.

@runtime_checkable
class PresenceProtocol(typing.Protocol):
361@runtime_checkable
362class PresenceProtocol(Protocol):
363    """
364    The PresenceProtocol represents the  contract between an arbitrary presence function
365    and the foo
366    """
367
368    def __call__(self, t0: float, t1: float) -> float: ...
369
370
371
372    def overlaps(self, t0: float, t1: float) -> bool: ...
373
374    @property
375    def onset_time(self) -> float: ...
376
377    @property
378    def reset_time(self) -> float: ...

The PresenceProtocol represents the contract between an arbitrary presence function and the foo

PresenceProtocol(*args, **kwargs)
1953def _no_init_or_replace_init(self, *args, **kwargs):
1954    cls = type(self)
1955
1956    if cls._is_protocol:
1957        raise TypeError('Protocols cannot be instantiated')
1958
1959    # Already using a custom `__init__`. No need to calculate correct
1960    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1961    if cls.__init__ is not _no_init_or_replace_init:
1962        return
1963
1964    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1965    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1966    # searches for a proper new `__init__` in the MRO. The new `__init__`
1967    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1968    # instantiation of the protocol subclass will thus use the new
1969    # `__init__` and no longer call `_no_init_or_replace_init`.
1970    for base in cls.__mro__:
1971        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1972        if init is not _no_init_or_replace_init:
1973            cls.__init__ = init
1974            break
1975    else:
1976        # should not happen
1977        cls.__init__ = object.__init__
1978
1979    cls.__init__(self, *args, **kwargs)
def overlaps(self, t0: float, t1: float) -> bool:
372    def overlaps(self, t0: float, t1: float) -> bool: ...
onset_time: float
374    @property
375    def onset_time(self) -> float: ...
reset_time: float
377    @property
378    def reset_time(self) -> float: ...
@dataclass(frozen=True)
class PresenceAssertion:
381@dataclass(frozen=True)
382class PresenceAssertion:
383    """
384
385    This is the fundamental, immutable construct of The presence calculus,
386    and asserts the presence of an element at a boundary over a continuous interval of time.
387
388    A presence is defined over a half-open interval $[t_0, t_1)$ on the real
389    line, where $t_0$ is the onset time and $t_1$ is the reset time.
390
391    The
392    following constraints must hold:
393
394    - $t_0 < t_1$
395    - $t_0 \\in \\mathbb{R} \\cup \\{-\\infty\\}$
396    - $t_1 \\in \\mathbb{R} \\cup \\{+\\infty\\}$
397    - $t_0 \\ne +\\infty$
398    - $t_1 \\ne -\\infty$
399
400    These rules ensure that the interval is well-formed, bounded on the left,
401    and open on the right. Intervals such as $[2.0, 5.0)$, $[-\\infty, 4.2)$,
402    and $[1.0, +\\infty)$ are allowed, as are the special case $[-\\infty,
403    +\\infty)$ and $[0,0),$ the empty presence.
404
405    With the empty presence as the only exception, intervals with zero or negative duration, or with reversed or
406    undefined bounds, are disallowed.
407    """
408    element: Optional[EntityProtocol]
409    boundary: Optional[EntityProtocol]
410    onset_time: float
411    reset_time: float
412    presence: Optional[PresenceProtocol] = None
413    observer: Optional[EntityProtocol] | str = "observed"
414    assert_time: Optional[float] = 0.0
415
416    def __post_init__(self):
417        """
418        Validates the temporal bounds of the presence interval
419        """
420        if self.onset_time >= self.reset_time:
421            if not (self.onset_time == self.reset_time == 0):
422                raise ValueError(
423                    f"Invalid interval: onset_time ({self.onset_time}) must be less than reset_time ({self.reset_time})")
424
425        if self.onset_time == float("inf"):
426            raise ValueError("Presence cannot begin at +inf")
427
428        if self.reset_time == float("-inf"):
429            raise ValueError("Presence cannot end at -inf")
430
431    def overlaps(self, t0: float, t1: float) -> bool:
432        return self.reset_time > t0 and self.onset_time < t1
433
434    def mass(self) -> float:
435        """
436        Returns the mass of the presence.
437
438
439        
440        """
441        onset = max(0.0, self.onset_time)
442        return max(0.0, self.reset_time - onset)
443
444    def mass_contribution(self, t0: float, t1: float) -> float:
445        if t0 >= t1 or not self.overlaps(t0, t1):
446            return 0.0
447        start = max(self.onset_time, t0)
448        end = min(self.reset_time, t1)
449        return max(0.0, end - start)
450
451    def __str__(self) -> str:
452        element_str = str(self.element) if self.element is not None else "None"
453        boundary_str = str(self.boundary) if self.boundary is not None else "None"
454        interval_str = f"[{self.onset_time}, {self.reset_time})"
455        return (
456            f"Presence(element={element_str}, boundary={boundary_str}, "
457            f"interval={interval_str}, provenance={self.observer})"
458        )

This is the fundamental, immutable construct of The presence calculus, and asserts the presence of an element at a boundary over a continuous interval of time.

A presence is defined over a half-open interval $[t_0, t_1)$ on the real line, where $t_0$ is the onset time and $t_1$ is the reset time.

The following constraints must hold:

  • $t_0 < t_1$
  • $t_0 \in \mathbb{R} \cup {-\infty}$
  • $t_1 \in \mathbb{R} \cup {+\infty}$
  • $t_0 \ne +\infty$
  • $t_1 \ne -\infty$

These rules ensure that the interval is well-formed, bounded on the left, and open on the right. Intervals such as $[2.0, 5.0)$, $[-\infty, 4.2)$, and $[1.0, +\infty)$ are allowed, as are the special case $[-\infty, +\infty)$ and $[0,0),$ the empty presence.

With the empty presence as the only exception, intervals with zero or negative duration, or with reversed or undefined bounds, are disallowed.

PresenceAssertion( element: Optional[pcalc.EntityProtocol], boundary: Optional[pcalc.EntityProtocol], onset_time: float, reset_time: float, presence: Optional[PresenceProtocol] = None, observer: Union[pcalc.EntityProtocol, NoneType, str] = 'observed', assert_time: Optional[float] = 0.0)
element: Optional[pcalc.EntityProtocol]
boundary: Optional[pcalc.EntityProtocol]
onset_time: float
reset_time: float
presence: Optional[PresenceProtocol] = None
observer: Union[pcalc.EntityProtocol, NoneType, str] = 'observed'
assert_time: Optional[float] = 0.0
def overlaps(self, t0: float, t1: float) -> bool:
431    def overlaps(self, t0: float, t1: float) -> bool:
432        return self.reset_time > t0 and self.onset_time < t1
def mass(self) -> float:
434    def mass(self) -> float:
435        """
436        Returns the mass of the presence.
437
438
439        
440        """
441        onset = max(0.0, self.onset_time)
442        return max(0.0, self.reset_time - onset)

Returns the mass of the presence.

def mass_contribution(self, t0: float, t1: float) -> float:
444    def mass_contribution(self, t0: float, t1: float) -> float:
445        if t0 >= t1 or not self.overlaps(t0, t1):
446            return 0.0
447        start = max(self.onset_time, t0)
448        end = min(self.reset_time, t1)
449        return max(0.0, end - start)
EMPTY_PRESENCE: PresenceAssertion = PresenceAssertion(element=None, boundary=None, onset_time=0.0, reset_time=0.0, presence=None, observer='empty', assert_time=0.0)