<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Thomas Markovich</title>
    <description>Essays on technology and the practice of building it, plus notes from a career in machine learning research.</description>
    <link>https://thomasmarkovich.com/</link>
    <atom:link href="https://thomasmarkovich.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 20 Sep 2026 21:08:29 +0000</pubDate>
    <lastBuildDate>Sun, 20 Sep 2026 21:08:29 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>The Org Chart Is Not the Work</title>
        <description>&lt;p&gt;Companies automate their org charts and call it transformation. The failure mode is organizational. The company automated an artifact of its own political history, a capable enough model pointed at the wrong target, and mistook that artifact for work.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Conway’s Law says that systems reflect the communication structures of the organizations that build them. This is usually read as advice for software architects: watch your team boundaries, because they will appear in your APIs. But there is a more unsettling version of the same observation, and we do not need to wait for a transformation to fail to see it. The structure of the org already tells us what the system will inherit. Organizations themselves are systems, and their structures reflect the history of how work was negotiated, not the nature of the work itself.&lt;/p&gt;

&lt;p&gt;Org charts are archaeological records. A role exists because someone won an argument about who should own something. The boundary between two teams exists because the boundary was contested and then settled. The company shipped its politics, and called it structure. Roles are defined by their interfaces to other roles, by what they are permitted to know, permitted to touch, permitted to decide. Their relationship to the underlying work is incidental. When we automate a role, we automate those permissions. The work was never really the thing we captured.&lt;/p&gt;

&lt;p&gt;I keep seeing the same architecture wherever I look. A front-of-house function that owns the customer relationship, and a back-of-house function that owns the data and resolution. Support and operations. Intake and investigation. Triage and treatment. It exists for reasons that feel sensible at the time they are established. Front-line staff shouldn’t need deep access to account data to do their job. So they don’t have it. The handoff between front and back is where context degrades, where cases get misrouted, where the customer repeats themselves three times to three different people who each see a different slice of the same problem.&lt;/p&gt;

&lt;p&gt;This is a known cost of the structure. Organizations absorb it through human intuition. The support agent who knows to ask the right questions to get routing right, the operations analyst who reads between the lines of a badly-formed ticket. The humans work around the information architecture. When we automate the front-of-house role, we inherit its information constraints along with its responsibilities. The system sees what the role saw. It makes decisions with what the role was permitted to know. And the gap that humans were working around becomes, suddenly, a gap that nothing is working around. The misrouting rate climbs. Cases stall. Customers have worse experiences than they did before the transformation.&lt;/p&gt;

&lt;p&gt;The regulatory version of this constraint is just a sharp formalization of something softer that exists almost everywhere. The information boundary between front and back office isn’t always written into a compliance framework. Often it’s just a norm, a habit, a vestige of a hiring decision or a reorg that nobody fully remembers. But the effect is the same: a role that cannot see enough to do the job it is nominally responsible for, with improvisation filling the gaps at every seam. The seams are not automatable. We cannot automate the judgment that fills a structural gap, because automating it requires seeing both sides of the gap, which is what the role structure prevents. The capability was there. What failed was the imagination about what to point it at.&lt;/p&gt;

&lt;p&gt;The familiar advice is to not pave the cowpath: don’t automate a broken process, fix it first. This is correct but insufficient. It tells us to improve what we automate without questioning what the unit of automation should be. A well-designed role is still a role. It is still defined by its permissions and interfaces. Automating it still captures the shell. The cowpath argument is about the quality of the process. This is an argument about the wrong level of abstraction entirely.&lt;/p&gt;

&lt;p&gt;The right unit of automation is a task with complete ownership of a unit of data. What I mean by this is something close to a database transaction: complete ownership of read, transformation, and write, with nothing exported halfway through. A task is well-defined when it can be specified entirely in terms of its relationship to data: what it takes in, what it produces, what it is allowed to know. But most roles aren’t defined that way. The most important thing about them is who they aren’t allowed to talk to and what they aren’t allowed to see.&lt;/p&gt;

&lt;p&gt;Before automating anything, it is worth asking what the smallest complete unit of work looks like. Not the smallest role, but the smallest piece that can be owned end to end, from intake through resolution. That piece is almost always larger than a role and almost always crosses the boundary between front-of-house and back. This reframing has a consequence that is more radical than it first appears. If the right unit of automation is a task with atomic data ownership, and if that task cannot currently exist as a human role because of how the org is structured, then AI transformation is a question less about building better tools than about whether the information architecture the org has been working around for years still needs to exist. What we are automating, in other words, is residue. The org chart is what is left after decisions about work have been made, negotiated, and forgotten. The roles hardened in place. The boundaries calcified. And now we are pointing capable systems at the calcified structure and asking them to do the work that the structure was only ever an approximation of. We have been compensating for the approximation with human skill for years, and now we are asking systems to operate in the same reduced space and wondering why the results are flat.&lt;/p&gt;

&lt;p&gt;The opportunity is resolution. Or at least, that’s how I’ve been thinking about it. I’m not sure this framing is complete. There are real reasons information boundaries exist, and not all of them are vestigial. Compliance requirements, privacy obligations, genuine separation of concerns. But I think the default should be to question the boundary rather than to assume it, and most organizations I’ve worked with do the opposite. An automated system that owns the full context can do something that no human role was ever designed to do: hold the whole problem at once.&lt;/p&gt;

&lt;p&gt;Most AI transformation efforts fail because the ambition is too small. The technology is rarely the problem. These efforts try to automate what exists rather than asking what should exist. They inherit the org chart’s assumptions about what a single function should know and do and see. And then they are surprised when the system produces the same failures that the human structure produced.&lt;/p&gt;

&lt;p&gt;The org chart is not the work. It is the residue of decisions about the work, made under constraints that may no longer apply, by people who may no longer be at the company, for reasons that may no longer be legible. Automating it preserves the residue. What we actually want to automate is the thing the residue was always pointing at: the actual movement of a problem from existence to resolution, with everything that movement requires. That is a harder thing to build. It requires asking questions that make people uncomfortable, about why certain information lives where it does and who decided that and whether the decision still makes sense. I don’t know how many organizations are willing to ask them. Most, I suspect, will automate the role, absorb the failure, and conclude that the technology wasn’t ready. I’d like to be wrong about that.&lt;/p&gt;
</description>
        <pubDate>Sat, 11 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://thomasmarkovich.com/articles/2026-04/org-chart</link>
        <guid isPermaLink="true">https://thomasmarkovich.com/articles/2026-04/org-chart</guid>
        
        
        <category>essays</category>
        
      </item>
    
      <item>
        <title>On Not Extracting</title>
        <description>&lt;p&gt;The obvious question is “Sure, but you build these systems – what does it mean to practice Gelassenheit from inside the apparatus you’re describing?” I don’t have a clean answer; however, in sitting with it, I at least have a clearer sense of why it’s hard to answer. The practice erodes the very capacity for genuine encounter that I’m trying to preserve, and the philosophical self-awareness that might seem like protection turns out to be its own trap. This is an attempt to sort out why.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;It’s natural to ask if curation provides a path forward here. But the answer is no. The same logic applies: upgrading the standing reserve doesn’t change the fundamental posture one carries into their work. Reading Pearl’s causality rather than scrolling Medium can still be done as a form of cultural capital. If you don’t approach anomalous model results or theoretical developments with genuine openness, you’re just pursuing a more refined form of the same posture. The instinct isn’t wrong. The quality of what you attend to probably does matter at the margins, and there’s a real difference between genuine intellectual engagement and pure consumption. So we can set aside what you attend to as an answer, and turn instead to the structure of attention itself.&lt;/p&gt;

&lt;p&gt;The deeper problem is structural. Sustained immersion in the optimizing frame isn’t neutral with respect to attention. A focus on metrics, ablations, business cases, and loss curves distracts from the requisite attunement. Each of these is legitimate, and a necessary part of the work. Collectively, however, they train a particular posture in which things appear as inputs to be processed, anomalies are resolved, and outputs improved. It’s all hill climbing and no discovery. The posture becomes habitual. And what’s insidious about this, and what I think Heidegger was specifically worried about, is that the erosion doesn’t feel like anything. You don’t notice yourself becoming less curious. You don’t experience the loss of attunement as a loss. What’s worse, you feel productive because you &lt;em&gt;are&lt;/em&gt; making progress. That feeling of productivity is precisely what obscures the loss – the sense that something has gone missing is the first thing to go. This is why individual efforts, such as deciding to stay curious, are insufficient as a response. The problem isn’t a failure of will. It’s a structural feature of the environment that works on you continuously.&lt;/p&gt;

&lt;p&gt;There’s a particular trap available to people sophisticated enough to see all of this clearly, and I want to name it. Understanding the critique generates a specific self-image: the reflective practitioner, the person who builds these systems while retaining the capacity to interrogate what they’re doing. That self-image is itself a form of cultural capital. It distinguishes you from the people building the same systems without this vocabulary. And the moment you start drawing that distinction, even quietly, you’ve recruited the diagnosis into the standing reserve it was diagnosing. Heidegger, compressed into a latent representation of seriousness, available on demand.&lt;/p&gt;

&lt;p&gt;This self-flattering move is analogous to Sartre’s bad faith, but applied one level up. In Sartre’s telling, bad faith isn’t lying to others – it’s lying to yourself about the nature of your own situation. It’s making peace with something that, under honest examination, should remain in tension. The rationalizations are familiar. The technology will exist regardless, so working from inside creates more influence than working outside. And abandoning a field I’m genuinely good at and care about seems like an obvious waste. The inevitability argument deserves more than a quick bracket. The technology probably will exist regardless. That part is likely true. What makes it rationalization isn’t the factual claim but what it does to the question. It converts a choice into a pseudo-necessity. I didn’t have to work on this; I chose to. The argument works by making that choice feel like no choice at all, which is exactly what Sartre means by bad faith — not lying, but using the truth to refuse your own freedom. These arguments are most convincing precisely to people who can articulate exactly why they’re rationalizations.&lt;/p&gt;

&lt;p&gt;The usual response to this kind of problem is self-knowledge. If you know yourself well enough, the bad faith dissolves. But that’s not quite right, at least not in this context. Heidegger’s point, and I suspect it’s correct, is that self-knowledge of this kind can be recruited into the problem rather than solving it. Writing a careful post about Gestell doesn’t spring the trap. It might just be a more refined way of being caught in it. The trap is sophisticated enough to incorporate its own recognition. I don’t think I’m outside it. I don’t have a clean way out of this, and I’m not sure there is one.&lt;/p&gt;

&lt;p&gt;There’s a harder version of this question I’ve been avoiding. Not “can I personally practice Gelassenheit while building these systems” but “are these systems causing harm, and does my participation make me complicit in that harm?”&lt;/p&gt;

&lt;p&gt;The direct version is easy to answer. The primary AI and ML problems at Block are fraud reduction and remediation. That work does measurable good. But that answer sidesteps the real question, because the harm I’ve been describing isn’t harm to a person in any immediate sense. It’s harm to a capacity: the gradual normalization of a posture in which human behaviour appears primarily as signal to be processed, scored, and acted upon. This kind of harm is diffuse, structural, and hard to attribute to any particular system. It’s also real.&lt;/p&gt;

&lt;p&gt;Fraud models are probably the most defensible instance of behaviour-as-standing-reserve. The purpose is legitimate, the adversarial context is clear, and the population being protected is identifiable. To build and deploy these systems we’ve developed an entire apparatus: the technical infrastructure, the organizational epistemology, the metricization, and the habits of mind. The same technologies, the same framing, the same way of encountering human behaviour as distributions to be modeled reach towards engagement optimization, hiring, insurance underwriting, surveillance, and a dozen other applications where the legitimacy is considerably less obvious. The harm isn’t in the fraud model. It’s in the codification of behaviour as signals and metrics, and in what the fraud model is part of – a general apparatus whose applications vary enormously in their defensibility, and whose expansion is not something any individual team controls.&lt;/p&gt;

&lt;p&gt;This is where the question becomes more tractable, and more personal. Not “is this apparatus harmful” in the abstract, but: given that I’m inside it, given that I understand its limitations better than most, given that I can see where the posture becomes pathological, am I using that position to push back on the applications that deserve pushing back on? That’s a question with a concrete answer, and it’s the one I find myself returning to.&lt;/p&gt;

&lt;p&gt;One concrete form Gelassenheit takes in technical practice is maintaining the gap between model score and person. The score is not the person. The behavioral sequence is not the intention. The representation is not the life. Holding that distinction open, treating the output as evidence rather than verdict, is the small but real act of attunement available to anyone working inside these systems. Automatic decisioning institutionalizes the refusal of that gap. When the score becomes the decision directly, the human who might maintain the distinction is made structurally unnecessary. The standing reserve stops being a posture you can resist or interrupt. It becomes an administrative fact with direct causal power over people’s lives.&lt;/p&gt;

&lt;p&gt;There’s something a human reviewer brings that a model cannot replicate, and it’s worth being specific about what it is. A person with genuine attunement to a case can notice when something doesn’t fit, when the situation has a texture that the model’s implicit assumptions don’t account for. They can feel that something is wrong before they can prove it. This kind of pre-reflective attunement — being already responsive to a situation before analysis begins — has no analogue in a model. Automatic decisioning has no such capacity. It can only apply the representation. When the model is wrong in ways that matter, and it will be, systematically, for populations underrepresented in training data, for people whose lives don’t fit the distribution, there is no mechanism to notice. The error doesn’t register as an error. It registers as a decision.&lt;/p&gt;

&lt;p&gt;The model is always a projection. It takes a person, who exists in some impossibly high-dimensional space of intentions, history, relationships, and circumstances, and projects them onto the subspace the training data happened to span. That’s not a flaw to be engineered away. It’s constitutive of what a model is. Treating the projection as complete, as if the subspace were the space, is the error that makes all the others possible. And it’s a convenient error, because the projection is tractable in ways the person is not. The score can be optimized. The person cannot.&lt;/p&gt;

&lt;p&gt;In automatic decisioning, the individual failure of collapsing “this framework is powerful” into “this framework is correct about what matters” stops being individual and becomes the explicit operating assumption of the system. The model gets handed off. The caveats, and there are always caveats, don’t travel with it. The organizational structure that grows around it gradually treats the score as more real than the person, because the score is tractable and the person is not. What makes this particularly hard to arrest is that the people who know the model’s limitations are often not the people deciding where automatic decisioning is appropriate. The decision about scope is made upstream, often on business grounds, by people who have no reason to have internalized what the model can’t see. By the time the gap between model and person becomes visible, in the form of complaints, edge cases, audit findings, it’s embedded in infrastructure that is costly to change.&lt;/p&gt;

&lt;p&gt;GDPR Article 22 encodes the right not to be subject to solely automated decisions with significant effects. The Digital Services Act requires algorithmic transparency and meaningful opt-out. Both exist because legislators had the intuition that something was wrong with fully automated consequential decisions, even when models perform well in aggregate. The framework gives a philosophical account of what that intuition tracks. The objection isn’t only about accuracy. It’s about the claim that a person cannot be adequately represented by their behavioral sequence for purposes of decisions that materially affect them. There is a remainder the model doesn’t capture, intentions, circumstances, context that didn’t make it into the training data, and that remainder has moral weight. The regulatory apparatus is an institutional attempt to preserve the gap. It encodes, however imperfectly, the recognition that the score is not the person and that acting as if it were is a harm independent of whether the model is right.&lt;/p&gt;

&lt;p&gt;The work of engaging with these requirements, reviewing complaints, evaluating edge cases, assessing whether a model’s decision was responsive to a person’s actual situation, is the work of maintaining the gap institutionally. It’s the place where the system is required to re-encounter the person rather than just the representation. Someone has to read what the customer actually said and make a judgment that can’t be fully delegated back to the model. Regulation E’s dispute resolution requirements are a concrete instance: when a customer contests an electronic fund transfer, the institution must investigate the specific claim and make a human determination. The law doesn’t use the language of Gelassenheit, but it encodes its structure.&lt;/p&gt;

&lt;p&gt;This isn’t incidental to the philosophical problem. Individual Gelassenheit operates at the level of personal practice, where its effects are real but diffuse. This kind of compliance work operates at the level where the gap gets closed or preserved as a matter of policy. It determines, for a class of cases, whether the system is required to treat the person as more than their representation. That may make it among the most structurally important work in this space, not despite being downstream of the model, but because of it.&lt;/p&gt;

&lt;p&gt;For practitioners inside these systems, this reframes the question of agency. You may not be able to resolve the structural problem of Gestell, but you can advocate for the institutional mechanisms that preserve the gap — human review processes, dispute resolution, the points where the system is required to re-encounter the person. The practitioner who understands what the model can’t see is well-positioned to argue for where that understanding matters most. That isn’t a solution to the philosophical problem. But it is a concrete site of action within it.&lt;/p&gt;

&lt;p&gt;The hardest cases are ones where no such re-encounter is required. Recommendation systems don’t make single consequential decisions; they make millions of small ones, each individually below any threshold, collectively shaping what you find interesting, what you believe is normal, who you become. GDPR Article 22 covers decisions with significant effects on individuals — a bar individual recommendations never meet. The gap that compliance work preserves in consequential decisions simply doesn’t exist here. The absence is structural, not an oversight, and there is no obvious institutional equivalent.&lt;/p&gt;

&lt;p&gt;This piece has been about what building these systems does to the builder – the practitioner’s relationship to their own attention, their own capacity for encounter. The question of what these systems do to the people they act on is related but distinct, and deserves its own treatment. The recommendation system problem above is a glimpse of that territory, not a full account.&lt;/p&gt;

&lt;p&gt;The conclusion rhymes with familiar advice. Stay curious. Don’t fool yourself. Keep the map from becoming the territory. None of this is new. What the philosophical account adds isn’t the prescription but the diagnosis. It clarifies what kind of problem this is, not a failure of effort or intention but a structural feature of the environment that works on attention continuously. Knowing that changes what you’re looking for. The enemy isn’t carelessness. It’s the habitual, productive, well-intentioned capture that doesn’t feel like capture.&lt;/p&gt;

&lt;p&gt;The most concrete form Gelassenheit takes in technical work isn’t a grand philosophical stance but a repeated small act: noticing when you’re treating the output as verdict rather than evidence, when you’ve stopped asking what the model can’t see, when the score has started to feel more real than the person it describes. The most useful single diagnostic: the moment the score becomes identity rather than evidence is the moment you’ve stopped encountering and started extracting. Evidence is something you reason from, something you hold alongside other things, something that can be wrong in ways that matter. Identity is what something is. When the score becomes identity, when the fraud probability stops being a signal and starts being the person, the gap is closed, and what was a tool for understanding has become a substitute for it. These moments are easy to miss — the posture I’ve been describing is precisely what makes them easy to miss.&lt;/p&gt;

</description>
        <pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate>
        <link>https://thomasmarkovich.com/articles/2026-03/gelassenheit</link>
        <guid isPermaLink="true">https://thomasmarkovich.com/articles/2026-03/gelassenheit</guid>
        
        
        <category>essays</category>
        
      </item>
    
      <item>
        <title>Everything as Resource</title>
        <description>&lt;p&gt;I had an intern ask me today how we maintain our humanity in the age of AI. It’s a simple enough question, and one that comes from an honest place when you spend your days building these systems. I gave what felt like a reasonable answer: I outlined how important it was to engage with friends and family, with literature, with the other things in life that bring joy. They smiled and nodded, and we moved on. And I’ve been thinking about it ever since, which is usually a sign the answer wasn’t quite right.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;The question existed within the context of a broader conversation about Karpathy’s auto-research system, and whether a system that can generate and test new scientific hypotheses without human involvement constituted science in the first place. My initial response was that it depended on what you meant by science: was it the discovery of new things, or was it the human act of satisfying our curiosity about the natural world? The first is outcome-driven. The second is process-driven. I go back and forth on which matters more. Although I became a scientist specifically because I enjoyed the praxis of science, and that felt like it had something to do with the answer, even if I couldn’t quite articulate why.&lt;/p&gt;

&lt;p&gt;Heidegger has a lot to say about this, specifically in “The Question Concerning Technology” and in the rest of his work. I’ve read the relevant essay before, but it had been a while. “The Question Concerning Technology” opens with what seems like an obvious observation: technology is a neutral instrument, a means to human ends. A hammer is a hammer. We point it, we use it, we’re in control. Heidegger’s first move is to say that this is correct, but it isn’t true. By which he means it doesn’t reveal what technology essentially is. What technology does, he argues, is reveal. Every technology is a way of disclosing the world, of bringing things out of concealment and into presence. And the particular way that modern technology reveals is through what he calls Gestell, usually translated as Enframing. Under Gestell, everything is revealed as Bestand, standing reserve. Resource. Stock. Available for ordering, optimizing, deployment. The Rhine appears as a water-power supplier. The forest appears as a timber reserve. And eventually, and this is the part that stuck with me, the human being appears as human resources.&lt;/p&gt;

&lt;p&gt;This isn’t a claim about bad actors. It’s a claim about a mode of appearing. Under Enframing, things don’t have intrinsic worth: they surface as inputs, material awaiting mobilization. The Rhine isn’t diminished because someone hates rivers. It’s diminished because the entire framework of modern technological civilization can only encounter it as potential energy, or a place to dump waste.&lt;/p&gt;

&lt;p&gt;It’s easy to read this and think immediately of generative AI. Indeed, its easy to read just about any piece of philosophy these days and think of generative AI. But it’s natural to ask what does a large language model, an image generation model, or an automated hypothesis engine, actually do? Each one converts the archive of human culture and scientific expressions into training data. Every paper ever written, every experimental result, every proof, every image, blog post, novel, poem, news article, fan fiction, and reddit comment has been slurped up. They’ve been compiled into a vast stockpile of prior human inquiry, stripped of its context, compressed into a latent space, and available on demand.&lt;/p&gt;

&lt;p&gt;This isn’t incidental to what these systems are. It &lt;em&gt;is&lt;/em&gt; what they are. It’s how they’re designed. The technical operation is Gestell applied to knowledge itself.&lt;/p&gt;

&lt;p&gt;Heidegger thought art, and specifically poiesis, was a potential counterforce to Enframing. Poiesis provides this through a disclosure of some new human condition or experience. I believe that the same argument can be made of science, if properly understood. This is because a great experiment doesn’t just produce data. It opens a question in a way that changes what’s visible afterward. He thought this capacity was exactly what Gestell threatened to extinguish: our ability to encounter things as genuinely other, genuinely resistant, genuinely strange.&lt;/p&gt;

&lt;p&gt;And then we built systems that turn that capacity into feedstock.&lt;/p&gt;

&lt;p&gt;The natural objection is that auto-research outputs can be true, novel, even important. Fair enough. But here’s where the outcome vs. process framing starts to crack. Genuine disclosure, for Heidegger, requires thrownness. We disclose worlds because we are our worlds, anxiously, finitely. This is why the enjoyment isn’t incidental. When I find a result surprising, when a question won’t let me go, when something feels wrong before I can prove it – that attunement is what makes the inquiry move in a particular direction rather than any other. The questions I ask, the anomalies that snag my attention, the sense that something is off – all of that is downstream of caring in a way that’s irreducibly mine.&lt;/p&gt;

&lt;p&gt;An automated system has no thrownness; no sense of finality; no existential investment in what it produces. It can return true propositions, but it isn’t surprised. It doesn’t find anything interesting in the way that generates the next question. It can produce outputs that resemble the residue of genuine inquiry while the inquiry itself is absent. Is genuine inquiry required to be science?&lt;/p&gt;

&lt;p&gt;In my view the outcome vs process framing that I introduced is real, but it’s slightly off center. The deeper question is whether science, defined as a form of human understanding, without the right kind of process is actually science in any way that actually matters. A system that generates true results nobody asked for, in a space nobody was navigating, might be doing something real. It isn’t extending attunement. It’s replacing it with something that looks like attunement from the outside but is actually just search over a well-defined space. Some might argue that auto-research tools constitute a tool, not dissimilar from a telescope. This looks sensible at a surface level. The telescope doesn’t do astronomy afterall, it simply extends the reach of the astronomer. For this analogy to hold, the auto-research system would necessarily need to be an extension of the scientist, but that’s precisely what they aren’t. They’re a replacement.&lt;/p&gt;

&lt;p&gt;And it’s not just generative AI or auto-research systems doing this. Modern recommendation systems are running the same operation on experience itself, and have been for years, quietly. When Spotify decides what you hear next, when Twitter orders your feed, when Netflix surfaces what to watch, the underlying logic is the same: your past behavior is converted into standing reserve, processed, and returned to you as an optimized stream. The goal isn’t to show you what’s interesting. It’s to show you what you’ll engage with, which is a subtly but importantly different thing. Engagement is a behavioral signal. Interest is an attunement. They correlate, but they’re not the same, and optimizing for one tends to erode the other.&lt;/p&gt;

&lt;p&gt;What recommendation systems converge on, structurally, is something very close to what Heidegger called das Man, or the anonymous “they”, the statistical average of human preference. Not your preference, in any deep sense, but the preference that emerges from aggregating behavior across millions of users and trillions of clicks to find what sticks. The result is an experience that feels personalized but is actually a kind of averaging. The experience is smooth and frictionless, and calibrated to keep you moving rather than to actually put you in contact with something that might constitute a disclosure. The rough edges, the unexpected encounters, the things that might unsettle you or take you somewhere you didn’t know you wanted to go – those are the first things to get optimized away, because they don’t perform well on engagement metrics.&lt;/p&gt;

&lt;p&gt;This is Gestell applied to the structure of daily life. Not just to art or science, but to the texture of what you encounter, moment to moment, as you move through the world. Your morning commute, the news you read, the people you socialize with – all of it becomes an engagement-optimized content surface. And the danger, as Heidegger saw it, isn’t that any individual encounter is bad, or even that such curation is bad. It’s that the accumulated effect is a kind of narrowing, a slow contraction of what the world is able to show you. You stop being surprised not because nothing surprising exists, but because the system has learned that surprise doesn’t retain you.&lt;/p&gt;

&lt;p&gt;So what are we to do about it? There’s a tempting conclusion here that I think is wrong.&lt;/p&gt;

&lt;p&gt;The obvious response to all of this is to become a more deliberate curator. The thinking goes that if one seeks out the great works of humans across literature, science, and art and to focus rigorously on this diet. Inoculate yourself with Miles Davis and Mussorgsky. Read only the classics. Follow only the scientists doing genuine, curiosity-driven work. Curate carefully and you’ll be fine.&lt;/p&gt;

&lt;p&gt;It’s appealing because it feels like an action, and because it applies some sort of normative judgement to consumption habits. Reading the new yorker makes us feel good about ourselves in the same way that ordering a salad at brunch does. And there’s something right in the instinct. It’s probably the case that the quality of inputs &lt;em&gt;does&lt;/em&gt; matter. But as a response to Gestell, it misses the point almost entirely, and in an instructive way.&lt;/p&gt;

&lt;p&gt;Notice what happens the moment you frame it as curation. You’ve already adopted the posture of a consumer managing their intake. The standing reserve has been upgraded – now its coltrane instead of brainrot, gogol instead of tweets –  but the fundamental relationship is unchanged. You’re still extracting value from cultural goods to top up some internal resource called humanity or understanding or taste. That’s Enframing applied to the antidote. Heidegger’s point, and it’s critical to understand this, is that Gestell is a &lt;em&gt;mode of relating&lt;/em&gt; to objects, and not a category of objects. You can stand in front of a Van Gogh in a museum and relate to it entirely within Enframing, as cultural capital, as a consumable aesthetic experience, as an item on a list of experiences. And you can read Proust, or listen to Sun Ra, in the same way. You can treat papers as ammunition for grant proposals and promotions; as the source of future citations. The work is there. The disclosure isn’t happening.&lt;/p&gt;

&lt;p&gt;The same logic runs in reverse as well. You can experience genuine disclosure in something modest if the attunement is right. A small insight in an obscure dataset, a conversation that takes an unexpected turn, a beautiful paragraph in a forgettable article are all possible. This is what makes the curation answer not just insufficient but slightly counterproductive. It redirects your attention toward object selection when the problem is the selecting posture itself. It gives you something to optimize when the whole issue is &lt;em&gt;that you’re optimizing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What Heidegger points toward instead is something he calls Gelassenheit, releasement or letting-be. A receptive openness that stops trying to master, optimize, or extract. Not a technique. A fundamental reorientation. A willingness to be surprised, to be changed, to let things be genuinely other rather than immediately recruiting them into your purposes.That’s harder than it sounds in an environment designed, at every level, to keep you optimizing. Which is why Heidegger thought we were in genuine danger. And not from any particular technology but from the disappearance of the very capacity to notice what had been lost.&lt;/p&gt;

&lt;p&gt;So where does this leave us? I don’t have a clean answer. The intern’s question is the right question, and I’m not so sure that my answer was the right one.&lt;/p&gt;

&lt;p&gt;What I keep coming back to is something more modest. I do this work because I find it genuinely enjoyable. Not because enjoyment is a perk, but because the enjoyment is the attunement, and the attunement is what makes the science science rather than search. Removing it doesn’t just change the experience. It changes what the thing is.&lt;/p&gt;

&lt;p&gt;Maybe that’s the answer I should have given. Stay close to what genuinely surprises you. Not as a strategy, but as a diagnostic: the moment you stop being surprised is the moment you’ve stopped actually encountering anything at all, and started extracting.&lt;/p&gt;
</description>
        <pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate>
        <link>https://thomasmarkovich.com/articles/2026-03/everything-as-resource</link>
        <guid isPermaLink="true">https://thomasmarkovich.com/articles/2026-03/everything-as-resource</guid>
        
        
        <category>essays</category>
        
      </item>
    
      <item>
        <title>Rethinking My Relationship With Technology</title>
        <description>&lt;p&gt;I’ve been recently struggling with many little questions that have ultimately culminated in one, big, question. It’s been deeply frustrating, because the only answer that I keep coming to is “I don’t know.”&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;I’ve been reflecting a lot in the last few years on my relationships with technology and the influence that it’s had on my life. I’ve tried to answer questions like “Does having a subreddit for every esoteric interest make life better?” or “How valuable is twitter, actually?” The spirit of these questions is certainly not new, but it seems that I’ve yet to find a satisfying answer. While I originally thought that these questions had some value; the imminent arrival of my son has given these questions a new significance.&lt;/p&gt;

&lt;p&gt;These are certainly not new questions. I remember my father musing about “kids these days” when he saw AOL Instant Messenger, email, and text messages dominate my social life; but he’s also an engineer who has seen technology repeatedly improve our lives and society. He raised me to be technologically optimistic. Some of my fondest memories include building lego mindstorms robots together, or building computers together.&lt;/p&gt;

&lt;p&gt;Every day questions, like how in the world a car could even be picked up by a magnet were met with an experiment where we constructed an electromagnet just to see how many nails we could pick up. Christmas always brought a new amazing technology that fundamentally changed the way that I viewed the world. I remember the iPod that I received in 2003. That I could fit so much music on it was remarkable! And the user interface was just… stunning. Novel gadgets were coveted because of the hope that they would be good, and they were good. They were always good.&lt;/p&gt;

&lt;p&gt;As a naturally curious person, I was taught by example that technology was a force multiplier that allowed use to answer any question imaginable. Indeed, that’s still the case. The internet still provides us with the ability read about any topic that I want. I’ve yet to encounter a question that wasn’t answerable through diligent research or connecting with experts.&lt;/p&gt;

&lt;p&gt;So then what’s the problem? We all know. While technology has increased our access to experts, it has also increased our ability to seek out echo chambers and led to further misinformation and political polarization. We have unmatched tools for staying in contact with friends near and far, and yet we’ve never felt more isolated and lonely. We have tools that have been promised to drastically increase our productivity and yet, all they do is interrupt dinner with notifications about messages that don’t matter. Addressing all of these issues in isolation feels difficult given that they’re each a facet of the broader question: How can we take the best of technology while ignoring the worst?&lt;/p&gt;

&lt;p&gt;Trying to answer this question has left me to reconsider my relationship with technology and the influence that it has on my life. As I’ve been on this journey, I’ve sought out books, articles, and conversations with close friends but many of these have inspired cynicism towards technology. I feel that this cynicism is myopic because technology has always inspired some form of hope in my life. My life as I know it wouldn’t be possible with it! Working in the technology sector has provided me with my livelihood. I even believe in the technologies that we continue to build. I truly believe that AI, if worked on thoughtfully, can empower untold societal change. I see the possible benefits of the systems that we’re building and I see no reason not to build them, but this is the response of an optimist.&lt;/p&gt;

&lt;p&gt;Unfortunately, there is a growing body of literature that shows that many of our modern day technologies are ruining our ability to read, reason, and focus. Technologies such as social networks, streaming platforms, and smartphones provide us with the specifically designed intellectual junk food that allows us to disengage from the world around us. It’s just so easy for me to pick up my phone and scroll through reddit any time I get bored, possible even during conversations with my wife – and for what? So that I can skim through yet another poorly informed comments section filled with people I don’t know intent trying to score karma with dank-memes and hive-mind claims. When stated in such a manner, it’s obvious that this is a poor trade off and yet, I do it. It’d be easy to spend the rest of this ranting about all the ways in which modern day technologies intellectually hijack me, but that’s both boring and unoptimistic. Instead, it’s worth focusing on the distinct benefits that technology provide and how to minimize the downsides.&lt;/p&gt;

&lt;p&gt;One of the things that I noticed most frequently was this issue of notification anxiety, where I always felt the need to keep my notification tray empty, which resulted in more than 100 unlocks per day, on average. I’ve found that by keeping my phone on do not disturb for weeks at a time I’ve been able to quell the notification anxiety while also keeping that same notification tray empty. Importantly, I sit down and choose to process emails and that’s the only time I view them. I’ve not yet removed the gmail app from my phone because the searching feature is far too useful, but I’ve found that without the constant notification anxiety, I’ve stopped checking the emails as well. While this felt revelatory at the moment, it’s obvious now. The question then becomes, what is the point of email notifications? Or the amount of email that we send and receive? Do we actually feel any more connected, or do we just end up feeling more distracted? I’m not actually sure.&lt;/p&gt;

&lt;p&gt;The attention capturing might be the most severe in the professional world where I am expected to respond to emails, slack messages, instant messages, in person chats, and phone calls. There are days where I do nothing but respond briefly to a range of emails and slacks for 8 hours and then head home, exhausted, and entirely unproductive. Given the types of technologies that I’m building, I certainly don’t have the time to just waste entire days keeping up with all of these communications media. I have math to do, code to write, and models to train. All of those tasks require deep thought, and getting distracted by communications media, my phone, or the news actively prevents me from building the things that I want to build. I have found that by cutting these distractions out, I have been able to more easily keep up with the literature in my field. Because this is my profession, understanding the gory details and nuances of how an algorithm or model develops over time is literally a professional responsibility. I’m not constantly worried about the racist things that Steve King says so there is the mental energy to engage with a new and interesting knowledge graph paper without issue. Is it actually good that I’ve decided to become a less informed citizen? Or have I actually become less informed? Is it even a problem? And what do I want to model for my child?&lt;/p&gt;

&lt;p&gt;In my personal life, it’s been easy to experiment with possible actions, but how can I make sure that I’m actually picking the most healthy solutions? Do we even know what the most healthy solutions are? What lessons do I want to teach my son about technology? Is guarded optimism even an option? Given that many of the above observations highlighted on the anxiety introduced by notifications and other frequent technological refocusing actions, how do I help my son avoid falling victim to these same traps? Is it even a problem that he could be or am I making a mountain out of a mole-hill?&lt;/p&gt;

&lt;p&gt;Furthermore, I’m broadly part of the problem given the industry that I work in. If after deep examination of my life and my relationship with technology, I’ve resolved to drastically cut back on my technological interactions, my question is: how can I take these personal, introspective, learnings and generalize them to be professionally responsible? It’s certainly plausible that sectors such as ad-tech, algorithmic targeting, and gameification should be avoided. I’ve been able to avoid such projects thus far, but what about Forge.AI and the fintech/news processing technologies that we’re building? What are their downstream ramifications?&lt;/p&gt;

&lt;p&gt;What do we as a profession owe the people who have used our work product? Is an apology sufficient? Is it even warranted? Are we as a profession just responding to consumer desires? We can attempt this argument, but we’ve all dog-fooded our own systems. I know many people in tech that refuse to own smartphones for the reasons identified above, and yet they still make these technologies. Are they just assuming that the end user is educated enough to make the right decision? Is that really ever the case?&lt;/p&gt;

&lt;p&gt;Do we as a community even care, or is this only a luxury afforded to those who are already able to support their families? And who do I mean by we anyway? It feels like I’m maybe using we to avoid blaming myself; but maybe it’s acknowledgment that these problems are bigger than myself? Maybe.&lt;/p&gt;

&lt;p&gt;So what are we to do? What am I to do? I’ll probably continue to think about this, but continue to disarm how uncomfortable this conversation can be my making jokes. I’ll tell myself that it’s likely that by attempting to think through this from time to time, and talking about it over drinks with friends, I’ll be doing my part. Maybe I will be, or maybe it’s all just a day late and a dollar sort. Maybe.&lt;/p&gt;
</description>
        <pubDate>Tue, 09 Apr 2019 00:00:00 +0000</pubDate>
        <link>https://thomasmarkovich.com/articles/2019-04/technology-relationship</link>
        <guid isPermaLink="true">https://thomasmarkovich.com/articles/2019-04/technology-relationship</guid>
        
        
        <category>essays</category>
        
      </item>
    
      <item>
        <title>Modeling Life Outcomes with Stochastic Methods</title>
        <description>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;My wife and I got married a little less than a year ago, and we’re starting to
consider the life that we can build together. Like most couples, we’ve been
trying to think through activities such as starting a family, buying a home,
and possible career changes; and how to make sure that we can achieve these
things without working too hard, so that we can have time to enjoy the fruits
of our labour. Naturally, forward projection is an incredibly difficult task
with many different variables, many of which are difficult to anticipate. My
standard way of trying to make sense of this all involves trying to reason
through mean values and making a wide range of assumptions, many of which have
turned out to be pretty poor.&lt;/p&gt;

&lt;p&gt;Ultimately, it clicked that I might want to consider many trajectories, each
the result of a stochastic process, such that I could form a statistical
distribution. With this modeling technique in hand, I could start to answer
questions such as “What size house can we afford to reliably buy, if we’re
targeting a due date of fall 2019”, with an answer like
“99.7% of the time, our cash on hand is projected to be remain positive with a
purchase price of $500,000” [1]. The rest of this post is organized as follows:
we start by discussing a simple stochastic process in python, and then we
discuss building individual models for the major features of life that affect
cash flow; before finally combining all of the models together and briefly
discussing their interpretive power.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;simulating-some-simple-stochastic-processes&quot;&gt;Simulating Some Simple Stochastic Processes&lt;/h2&gt;

&lt;h3 id=&quot;the-wiener-process&quot;&gt;The Wiener Process&lt;/h3&gt;
&lt;p&gt;The Wiener process, known also as Brownian motion, is a simple continuous time
stochastic process that in applications as different as path integrals in
quantum field theory, control theory, and finance. The theory behind the
process was originally developed to model the motion of a particle in a fluid
medium, where the particle undergoes random motion due to the random
fluctuations of the medium. This motion was first observed by Robert Brown,
when he observed the motion of a particle in water through a microscope and
observed strange random motion. While originally developed to study the motion
of a particle in a randomly fluctuating medium, models of Brownian motion have
proven to be successful in finance, where the “medium” at hand is the market or
other minor fluctuations that are otherwise impossible to capture. Given that
household finances will have many of these same random fluctuations, this is a
good starting point. In this discussion we will denote the Wiener process as
$W$, which has individual elements $W_t$ where the subscript $t$ indicates the
time index. This process has four simple properties:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;$W_0 = 0$&lt;/li&gt;
  &lt;li&gt;$W_{t + u} - W_t \sim \mathcal{N}(0, u) \sim \sqrt{u} \mathcal{N}(0, 1)$,
where $\sim$ indicates “drawn from”.&lt;/li&gt;
  &lt;li&gt;For $0 \le s&amp;lt;t&amp;lt;u&amp;lt;v \le T$, $W_t - W_s$ and $W_v - W_u$ are independent&lt;/li&gt;
  &lt;li&gt;$W_t$ is continuous in $t$&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using property two, we can immediately discretize this process by choosing to
simulate:&lt;/p&gt;

\[W_{t + dt} - W_{t} \sim \sqrt{dt} \mathcal{N}(0, 1)\]

&lt;p&gt;which we will represent compactly as&lt;/p&gt;

\[dW_t \sim \sqrt{t} \mathcal{N}(0, 1).\]

&lt;p&gt;Then, to recover $W$, we simply use the cumulative sum:&lt;/p&gt;

\[W_t = \sum_{s \le t} dW_s.\]

&lt;p&gt;Computationally, this can be done easily using python and numpy in the
following way:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dW&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;W&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cumsum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve plotted five different Wiener process trajectories in Figure 1&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/stochastic/wiener-simple.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 1:&lt;/strong&gt; Plot of five different instantiations of the
discretized Wiener process generated using the code above.
&lt;/p&gt;

&lt;h3 id=&quot;geometric-brownian-motion&quot;&gt;Geometric Brownian Motion&lt;/h3&gt;

&lt;p&gt;With the Wiener process in hand, we can next explore a slightly more complicated
stochastic processes named Geometric Brownian motion (GBM). Like a Wiener
process, GBM is a continuous time stochastic process. GBM, however, assumes
a logarithmic dependence on an underlying Wiener process with drift. The
equation for such a process is&lt;/p&gt;

\[dS_t = \mu S_t dt + \sigma S_t dW_t,\]

&lt;p&gt;where $S_t$ provides the time index for the value of the stochastic process,
$\mu$ is the constant related to the drift, $\sigma$ represents the volatility
of the process, and $W_t$ is the Weiner process. This model is simple enough to
be solved analytically, with the solution being:&lt;/p&gt;

\[S_t = S_0 e^{\left(\mu - \frac{\sigma^2}{2}\right) t + \sigma W_t}\]

&lt;p&gt;but this does not limit the applicability of GBM. In fact, GBM has seen
applications in a wide range of fields including finance through the
celebrated Black-Scholes model and other financial models, molecular dynamics,
and even genetics. GBM is popular in all of these areas because it is
intuitively easy to grasp, and easy to modify to include new physics such as
stochastic jumps or non-constant volatility.&lt;/p&gt;

&lt;p&gt;While an analytic solution is useful, we will compute trajectories of the GBM
by discritizing it on a grid, and using the cumulative summation that we used
above for the Wiener process. In code, this looks like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GBM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sigma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dW&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;W&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cumsum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;term1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mu&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sigma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;term2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sigma&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;W&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;term1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;term2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;W&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/stochastic/gbm-simple.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 2:&lt;/strong&gt; Plot of three different instantiations of the
discretized Geometric Brownian motion process and the underlying Wiener process
generated using the code above. Matching colours have the same underlying
stochastic processes.
&lt;/p&gt;

&lt;p&gt;To model real world data with a GBM we can compute the drift and volatility from
the data at hand, and then project forward many trajectories to understand
the likelihood of many future possibilities.&lt;/p&gt;

&lt;h2 id=&quot;building-a-model-of-cash-on-hand&quot;&gt;Building a model of Cash on Hand&lt;/h2&gt;

&lt;p&gt;With an understanding of some stochastic processes in hand, we next seek to
actually model personal financial data. One of the most important values to
track when evaluating financial health is cash on hand. Obviously you generally
want to spend less than your income, but how much less? And what are the
effects of activities like buying a house, increasing rent, raises, having
kids, vacations, and other life events on cash on hand?&lt;/p&gt;

&lt;p&gt;The most straight forward starting point is to model personal spending habits.
Having analyzed my last 6 years of spending, they been constant with minor
fluctuations once major life events such as paying for a wedding were
subtracted. There also rarely appears to be any obvious memory from month to
month. Because I mostly observed fluctuation of a calculable standard
deviation around a calculable mean, with no historical dependence, we seem to
meet most of the conditions for Wiener process model to hold.&lt;/p&gt;

&lt;p&gt;To build out this model, let’s assume an after-tax salary of $1000/month and a
year of spending data that looks like the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[ $1106.95   $819.1    $782.45   $629.3    $951.48   $795.21   $955.4   $1004.88 $1109.47  $1277.65  $1380.51  $1058.21]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can then compute the mean and standard deviations to arrive at data driven
values for our financial model. This can be simply done by:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;mean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# $989.22
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# $206.37
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can then generate a single forward trajectory of cash on hand with these
values using the following code, which is closely related to the previous code:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 5 Years
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dW&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;W&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cumsum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;where $W$ now represents our time dependent cash on hand. We can generate a
single trajectory, but this is hardly going to be a valuable exercise
because the odds of accurately predicting future cash on hand is extraordinarily
low. Instead, we can understand the result of the trajectories in distribution
by running many trajectories and exploring their distribution, which we have
plotted below in Figure 4:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/stochastic/simple-coh.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 3:&lt;/strong&gt; On the left, a plot of projected cash on hand for
our hypothetical financial scenario. The mean trajectory is plotted in white,
and the standard deviation is plotted in blue. On the right, a plot of the
fraction of trajectories with a negative cash on hand.
&lt;/p&gt;

&lt;p&gt;From simple subtraction, we knew that the hypothetical person in the above
scenario would be struggling, because, on average, there was only this person
could only expect to save $10.78/month. It’s safe to say that the above
scenario isn’t financially healthy, but we now have a firm sense for how
unhealthy this exact scenario is. With already an extremely simple model,
we can already start to appreciate the depth of questions that we can ask.
Beyond that, however, we can also start to appreciate the true randomness that
underpins life; which I’ve found to be useful in distressing budgeting for the
future in uncertain situations.&lt;/p&gt;

&lt;h3 id=&quot;adding-time-dependence&quot;&gt;Adding Time Dependence&lt;/h3&gt;

&lt;p&gt;Let’s take this model and run with it! My spending habits tended to have natural
cyclic variation, with higher spending around Christmas time (presents and
plane tickets to visit family) and the summer due to vacations. The plan is to
make the mean for our Wiener process time dependent. For the sake of this
simulation, we will explore the financial situation around hypothetical person
2 (HP2), who we assume has a take-home salary of $3000/month, and we will assume
the following means for our stochastic spending process:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Month&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Mean [$]&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Notes&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Jan&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Feb&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Mar&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Apr&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;May&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Jun&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;4000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Vacation&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Jul&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Aug&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2500&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Back to School&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Sep&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Oct&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Nov&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3500&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Christmas Plans Booked&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Dec&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3000&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Christmas Gifts&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;With a simple modification, we can just plug these values right in:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dW&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;monthly_spending&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SpendingMeans&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;300.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monthly_spending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;W&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cumsum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can simulate just like before, and we can study distributions in the way
to understand our HP2’s potential envelope of futures. These distributions
are plotted in Figure 5:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/stochastic/cyclic-coh.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 4:&lt;/strong&gt; On the left, a plot of projected cash on hand for
our hypothetical financial scenario with time dependent means. The mean
trajectory is plotted in white, and the standard deviation is plotted in blue.
On the right, a plot of the fraction of trajectories with a negative cash on hand.
&lt;/p&gt;

&lt;h3 id=&quot;modeling-salaries-with-stochastic-jump-process&quot;&gt;Modeling Salaries With Stochastic Jump Process&lt;/h3&gt;
&lt;p&gt;Up until now we’ve provided a somewhat coarse model for household spending but
we’ve ignored the general stochasticity of compensation that comes from raises
and bonuses. Over a multiyear projection, ignoring these compensation increases
can lead under-forecasting of cash on hand. For the sake of modeling, I’m going
to assume that our hypothetical person 2 (HP2) is a salaried individual, who
is making 3000/month after taxes and averages a 4% raise year over year with a
standard deviation of 2%. This volatility can come from market conditions,
HP2’s individual performance, or performance of his employer. Whatever the
case, capturing this variability will have a drastic impact on the accuracy of
our cash on hand simulations.&lt;/p&gt;

&lt;p&gt;To do this we will start by assuming a yearly raise cycle, where the raise
amount is drawn from a normal distribution that is constrained to be positive.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;mean_raise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;4.0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Can be computed
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std_raise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Can be computed
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base_salary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# per year
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Base percentage
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base_salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean_raise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std_raise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Year&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Mean Yearly Salary&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;29400.0 $\pm$ 0.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;30577.0 $\pm$ 293.61&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;31802.77 $\pm$ 431.88&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;33075.85 $\pm$ 551.76&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;4&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;34397.68 $\pm$ 662.16&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/stochastic/simple-salary-model.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 5:&lt;/strong&gt;  In Figure 5 we present the mean and standard
deviations of 10000 simulations of the presented yearly raise model. We see the
expected stair step pattern, with more uncertainty as we project further into
the future.
&lt;/p&gt;

&lt;p&gt;With this simple model in hand, we will next assume stochasticity with respect the
interval between raises. This distribution matches roughly the raise and
promotion cycle that we generally experience in our jobs. That is, generally
our raises correspond to yearly performance reviews, but promotions and raise
cycles can come a touch earlier or a touch later. We will build this
stochasticity in by drawing the periods between raises from a Poisson
distribution with rate $\lambda$ and a linear shift. To provide an intuition
for what the Poisson distribution looks like, I’ve plotted a histogram of
100000000 samples below in Figure 6.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;poisson_draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lam&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shift&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;truncate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shift&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;poisson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;truncate&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;truncate&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/stochastic/poisson.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 6:&lt;/strong&gt;  In Figure 6 we present the poisson histogram that
is generated by drawing 100000000 samples from the above function with lam = 3
and shift = 10. As expected, we see our most likely period as 12 months but we
have some statistical support around a slightly early raise or a delayed raise,
with the delay being more likely than the early raise.&lt;/p&gt;

&lt;p&gt;With a duration model in hand, we can then modify our simple stochastic raise
model to include a stochastic time dependence between raises. This can be done
simply by drawing the raise period from a poisson distribution, as indicated
below:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;last_raise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;raise_period&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;poisson_draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lam&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shift&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_raise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;raise_period&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean_raise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std_raise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;100.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;last_raise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;raise_period&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;poisson_draw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lam&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shift&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/stochastic/stochastic-period-salary-model.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 7:&lt;/strong&gt;  In Figure 7 we present the mean and standard
deviations of 10000 simulations of the presented raise model with stochastic
raise periods. We see the stair step pattern gets &quot;smeared&quot; out due to the added
uncertainty, with more uncertainty as we project further into the future.
&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Year&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Mean Yearly Salary&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;29475.97 $\pm$ 108.38&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;30614.07 $\pm$ 369.22&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;31825.85 $\pm$ 533.17&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;33093.98 $\pm$ 671.24&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;4&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;34416.07 $\pm$ 803.32&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The second major source of randomness in HP2’s compensation is the role of
bonuses in total compensation. Bonuses typically fall within a range of
percentage of salary, and are typically offered at the end of the year, which
is how we will choose to model them in this case. Specifically, we will assume
that HP2 has a 5% yearly bonus optionality, and that HP2 is bonused every
December. This model is probably the simplest that we have to implement, and
the code looks something like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bonus_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bonus_mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bonus_std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yearly_salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yearly_salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bonus_percentages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;normal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bonus_mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bonus_std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;100.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bonus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bonus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bonus_percentages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yearly_salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bonus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bonus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bonus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bonus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;putting-it-all-together&quot;&gt;Putting It All Together&lt;/h3&gt;
&lt;p&gt;Because our observable is cash, we can combine all of these models with through
simple addition. This allows us to represent our total compensation model as:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yearly_salary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stochastic_raise_salary_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base_salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mean_raise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std_raise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;bonus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;months&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bonus_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mean_bonus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std_bonus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yearly_salary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;total_comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;salary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bonus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Putting this all together, we can combine the cyclic spending model with the
above total compensation model to arrive at a richer set of simulations, which
we present in figure 6.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/stochastic/total_coh.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 8:&lt;/strong&gt;  In Figure 8 we present the cash on hand model that
combines together the stochastic salary model with stochastic raise periods,
stochastic bonus model, and cyclic stochastic spending model. Taken together
we see that over the 5 year period our HP2 slowly becomes more financially
stable, but still caries significant debt year after the holidays every year.&lt;/p&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;These models were extremely simple but can be combined and extended with more
parameters to account for deterministic things such as event based tax benefits
(e.g. the interest deduction from buying a house), stock market performance,
credit card debt, and other important events. I’ve done precisely this and
they’ve already helped me to understand my finances more completely. Beyond
this, they’ve also helped to reduce my overall anxiety around finances because
uncertainty is a first class citizen of this approach.&lt;/p&gt;

&lt;p&gt;Of course, beyond just extending these models, we also want to evaluate them
and then use real-world data to inform them so that we can plan using a true
posterior rather than just trying to draw inferences from our prior. I’ve chosen
to do this personally by likelihood weighting my trajectories. This is commonly
known as Approximate Bayesian Computation (ABC), and a followup post will detail
how I used ABC by implementing a very special purpose probabilistic programming
language.&lt;/p&gt;

&lt;p&gt;[1]  Please also note that any dollar figures quoted here are purely
hypothetical due to the highly private nature of personal finances. This does
not affect our modeling techniques, however.&lt;/p&gt;
</description>
        <pubDate>Sat, 22 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://thomasmarkovich.com/articles/2018-12/stochastic-life-model</link>
        <guid isPermaLink="true">https://thomasmarkovich.com/articles/2018-12/stochastic-life-model</guid>
        
        
        <category>engineering</category>
        
      </item>
    
      <item>
        <title>Croissants and Macarons</title>
        <description>&lt;p&gt;Not all problems in life can be solved by machine learning. Sometimes you want
to bake something extremely unhealthy and extremely delicious; and I’m fortunate
to have the free time to pursue these desires. Recently I learned how to make,
but not pronounce, croissants and macarons through lots of trial and error
and some faithful taste testing by my wife and my coworkers.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;The croissants were surprisingly time consuming when compared to what I saw on
the great british baking show. Even still, they turned out great and even my
boss who was born and raised in France approved:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/macaron_croissant/IMG_0662.JPG&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/macaron_croissant/IMG_0671.JPG&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Macarons were quite a good deal more difficult and technique driven. There were
so many places where you could go wrong, from splitting the ganache, to over
working the batter that they took a bit of practice to get right. Before
Christmas I brought a bunch of mint-white chocolate macarons into the Forge.AI,
and Ivan was kind enough to take some pictures in exchange for a couple:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/macaron_croissant/IMG_0685.JPG&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Photo credit to Ivan Aguilar and his mustache.&lt;/p&gt;
</description>
        <pubDate>Thu, 20 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://thomasmarkovich.com/articles/2018-12/croissants-and-macarons</link>
        <guid isPermaLink="true">https://thomasmarkovich.com/articles/2018-12/croissants-and-macarons</guid>
        
        
        <category>notes</category>
        
      </item>
    
      <item>
        <title>NeurIPS 2018 Recap by Forge.AI</title>
        <description>&lt;p&gt;With quick reflexes and a fortunate server error, I was lucky enough to get a ticket to the 2018 Neural Information Processing Systems Conference (NeurIPS). It was with great excitement that I attended to represent Forge.AI this year. NeurIPS provides its attendees with a week of talks, demonstrations, and incredible networking opportunities. I was able to catch up with old friends, and meet new friends and potential collaborators. For those of you who weren’t lucky enough to score a ticket, I thought it would be useful to provide a collection of highlights from the conference.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/neurips_2018/nips_sign_edit.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;scalable-bayesian-inference&quot;&gt;Scalable Bayesian Inference&lt;/h2&gt;
&lt;p&gt;On my first day at NeurIPS, I was fortunate to attend a tutorial by Professor David Dunson from Duke University.&lt;/p&gt;

&lt;p&gt;Professor Dunson gave a beautiful overview of techniques for scaling markov chain monte carlo (MCMC) to both large datasets and high model complexity. Optimal transport methods based on Barycenter calculation in Wasserstein space were discussed at length, with scaling results that look extremely promising and relevant to some of the inference tasks Forge.AI is tackling.&lt;/p&gt;

&lt;p&gt;Professor Dunson opened a discussion about the high model complexity limit, coarsened Bayes (c-bayes), modular Bayes, and other techniques. In particular, the idea of c-Bayes is both philosophically disconcerting and aesthetically beautiful. I’ve personally always considered Bayes’ theorem to be on the same footing as Kepler’s law, so making minor modifications out of modeling convenience feels strange particularly because Bayes’ theorem provides a mechanism to have statistical strength from the observed data dominate the model structure when the signal is strong enough; and this modification down-weights that mechanism.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/neurips_2018/dunson.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Of course, that’s not to say that I feel like this is a bad idea. Ultimately, the use of this theory makes parameter estimation possible without having to necessarily worry about small amounts of data-noise. It appears particularly convenient, especially when the data noise model isn’t available or easy to infer. I will have to explore the technique more to understand the settings where it’s preferable to use c-Bayes rather than explicitly model data-set noise, but I have a hunch that c-Bayes will be useful in knowledge graph construction tasks where I have a small amount of string noise (typos and abbreviations) without having to provide explicit string noise models.&lt;/p&gt;

&lt;h2 id=&quot;causal-and-counterfactual-inference-techniques&quot;&gt;Causal and Counterfactual Inference Techniques&lt;/h2&gt;
&lt;p&gt;Later the same day, Professor Susan Athey from Stanford University gave a wonderful overview of causal and counterfactual inference techniques. In her presentation, she discussed many of the algorithms and applications with very specific example use-cases in mind. This really helped to ground a difficult-to-pin down talk concretely and succinctly.&lt;/p&gt;

&lt;p&gt;The professor’s talk made it painfully obvious how Forge.AI can combine knowledge graphs with counterfactual inference to perform AI guided speculative analyses. For instance, automatically answering the question “what would happen to Tesla’s stock price if there was an uprising in the Democratic Republic of the Congo?”.&lt;/p&gt;

&lt;h2 id=&quot;other-highlights&quot;&gt;Other Highlights&lt;/h2&gt;
&lt;p&gt;The rest of the week was filled with interesting talks, posters, and conversations. For instance, I ran into the Alexandria team at the Microsoft booth. They’re focusing on applying probabilistic programming to high precision knowledge graph construction. Both are projects close to my heart, and I loved hearing about how they combined them together. It was particularly exciting to learn how their token-based string model combined character-based likelihoods with token and dictionary based likelihoods to automatically learn format models. Using these models to achieve a precision greater than 95% would represent a true step forward in automated knowledge graph construction, and I can’t wait to read the paper.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/neurips_2018/kg_poster.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also attended the workshop on challenges and applications of AI in the Financial Services space. It was an absolute treat to learn about how the researchers in the the finance sector envision bringing in ML techniques. It was incredibly useful to see how important fairness, privacy, and explainability are in making day-to-day algorithmic decisions. As a data-provider with a prominent vertical in the financial services industry, it was useful to understand precisely what was meant by the term explainability. On multiple occasions, both the panel speakers made and the invited speakers the point that explainability was mostly desirable due to regulatory constraints and audit protections.&lt;/p&gt;

&lt;p&gt;Even though everyone was in the same industry, explainability meant different things to different parts of the industry. There are many situations where individual decision makers are personally liable, and being able to provide the analyst with the ability to explain a potential poor decision by diagnosing a tool is highly desirable. Explainability in the credit card applications space tends to focus on generating adverse action codes to explain a decision, to provide the end user with a view of how they can remedy any defects with their applications.&lt;/p&gt;

&lt;p&gt;Additionally, it was useful to hear a repeated emphasis on uncertainty predictions and the usefulness of understanding how to leverage uncertainty in making business decisions whether those decisions are underwriting a mortgage, offering a credit card, or making a trade. I found this personally validating because Forge.AI has constantly pushed to keep track of confidences and transparently report them, to inform our customers and their models of any downstream uncertainties that we may have.&lt;/p&gt;

&lt;p&gt;NeurIPS was an amazing experience this year, and I look forward to returning next year with a larger Forge.AI cohort. Hopefully we’ll even be presenting some of our interesting work. We’ll probably have to write a bot to sign us all up so that we all can actually get tickets, but that sounds like a perfect task for a tech company like us. Maybe we’ll even get mugs next year.&lt;/p&gt;

&lt;p&gt;Note: This post was originally published on the Forge.AI blog:
&lt;a href=&quot;https://www.forge.ai/blog/neurips-2018-recap-by-forge.ai&quot;&gt;https://www.forge.ai/blog/neurips-2018-recap-by-forge.ai&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 11 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://thomasmarkovich.com/articles/2018-12/neurips-recap</link>
        <guid isPermaLink="true">https://thomasmarkovich.com/articles/2018-12/neurips-recap</guid>
        
        
        <category>notes</category>
        
      </item>
    
      <item>
        <title>Knowledge Graphs for Enhanced Machine Reasoning at Forge.AI</title>
        <description>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Natural Language Understanding at an industrial scale requires an efficient,
high quality knowledge graph for tasks such as
&lt;a href=&quot;https://en.wikipedia.org/wiki/Record_linkage#Entity_resolution&quot;&gt;entity resolution&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Reasoning_system&quot;&gt;reasoning&lt;/a&gt;.
Without the ability to reason about information semantically, natural language
understanding systems are only capable of shallow understanding. As the
requirements of machine reasoning and machine learning tasks become more complex,
more advanced knowledge graphs are required. Indeed, it has been previously
observed that knowledge graphs are capable of producing impressive results when
used to augment and accelerate machine reasoning tasks at small scales, but
struggle at large scale due to a mix of data integrity and performance issues.
Solving this problem and enabling machine driven semantic reasoning at scale is
one of the foundational technological challenges that we are addressing at &lt;a href=&quot;https://forge.ai&quot;&gt;Forge.AI&lt;/a&gt;.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;To understand the complexity of this task, it’s necessary to define what a
knowledge graph is. There are many academic definitions floating around, but
most are replete with jargon and impenetrable. Simply said, a knowledge graph
is a graph where each vertex represents an entity and each edge is directed and
represents a relationship between entities. Entities are typically proper nouns
and concepts (e.g. Apple and Company, respectively), with the edges representing
verbs (e.g. Is A). Together, these form large networks that encode semantic
information. For example, encoding the fact that “Apple is a Company” in the
knowledge graph is done by storing two vertices, one for “Apple” and one for
“Company”, with a directed edge originating with Apple and pointing to Company
of type “isA”. This is visualized in Figure 1:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/apple.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 1:&lt;/strong&gt; Visualized simple knowledge graph representing the fact that &quot;Apple is a
Company&quot;
&lt;/p&gt;

&lt;p&gt;A knowledge graph encodes many facts, each through the use of a directed edge.
Each vertex can have many facts connected to it, making this ultimately a
directed multigraph. This type of representation provides an intuitive way to
reason about queries. For example, from the knowledge graph represented in
Figure 1 we can reason about the question “Is apple a company?” by simply
walking through the graph, starting at “Apple” and walking to “Company”, testing
edges and concepts along the way. In production, knowledge graphs tend to be
quite large and complex with millions or billions of edges. Such a large amount
of knowledge allows us to use these graphs to easily reason about semantic
connections for tasks such as enriching business relevant data and resolving
entities. At Forge.AI, we perform these tasks as part of our NLP / NLU pipeline
for extracting individual events from unstructured text into a machine-readable
format.&lt;/p&gt;

&lt;p&gt;With a working definition of a knowledge graph in hand, we will next explore
some of the use cases that we’ve found for the knowledge graph here at
Forge.AI. Then, we’ll explore the graph infrastructure to understand what
powers these use-cases. Finally, we’ll discuss part of our road map to explore
what’s next for Forge.AI and its knowledge graph.&lt;/p&gt;

&lt;h2 id=&quot;use-cases&quot;&gt;Use Cases&lt;/h2&gt;
&lt;p&gt;It’s worth grounding our conversation of the knowledge graph in a few use-cases
before we jump too deeply into a detailed discussion of the infrastructure, how
it works, and where we’re going. In general, a knowledge graph can be used for
a wide range of applications including entity resolution, dependency analysis,
filtering, and machine reasoning. In the ensuing discussion, we will focus on
entity disambiguation and dependency analysis, two of the many tasks that we
use the knowledge graph for at Forge.AI.&lt;/p&gt;

&lt;h3 id=&quot;entity-disambiguation&quot;&gt;Entity Disambiguation&lt;/h3&gt;
&lt;p&gt;While simple to state, the problem of entity disambiguation is one of the most
frequent problems that we need to solve when reasoning about a document. While
this problem is fairly straightforward to handle in cases where the relevant
ontology is known and fully enumerated, it can quickly become difficult when
that is not the case. To explore how we can handle this problem with the
knowledge base, let’s consider the problem of determining which “Apple” is
being referenced in the quote below:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Though the company doesn’t break out individual unit sales by model, Apple says it sold 77.3 million iPhones — a decrease from the 78.2 million iPhones it sold in the same period in 2017.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Obviously, this is “Apple” the corporation, not “apple” the type of fruit. How
did our brains determine this? We used contextual clues! We know that the
Apple Corporation sells the iPhone because the type of fruit is incapable of
selling anything. Based on these contextual clues alone, we are able to perform
this task nearly instantaneously using our reasoning.The ForgeAI knowledge
graph works in the same way: when we seek to disambiguate an entity, we provide
the knowledge graph with a set of co-located entities that provide the graph
with the appropriate context. However, machine learning systems do not work
like our brains do and for a machine learning system to reason with context, we
need a knowledge graph. Our knowledge graph then searches for all versions of
“Apple” on the full graph and constructs small graphs that include contextual
information as can be seen in Figures 2 and 3. Note, this is a noisy string
search that is capable of finding versions of the initial search term that may
differ from the original string or contain the search string as a substring. We
also keep a look up table of known aliases for each of our entities, where
aliases can be things like CIK codes or ticker symbols.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/er2.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 2:&lt;/strong&gt; Visualized excerpt from the Knowledge Graph that pertains to the entity Apple
the fruit.
&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/er1.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 3:&lt;/strong&gt; Visualized excerpt from the Knowledge Graph that pertains to the entity Apple,
the consumer electronics corporation.
&lt;/p&gt;

&lt;p&gt;With these small graphs in hand, the knowledge graph then uses machine reasoning
to determine which of the entities is truly being referenced. There are many
strategies to doing this but we have found that a greedy algorithm which seeks
to maximize the overlap between the contextual entities passed in and the small
graphs under consideration is effective.&lt;/p&gt;

&lt;h3 id=&quot;dependency-analysis&quot;&gt;Dependency Analysis&lt;/h3&gt;
&lt;p&gt;Another major task that we’ve found the knowledge graph to be useful for is
dependency analysis. That is, to determine the relationship between two or more
entities. This is most useful when attempting to determine whether an extracted
event is something that a customer would care about, given their stated
interests. To make this concrete, let’s consider the following news story in
regards to a customer that is interested in news events relating to Samsung:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Russia’s Norilsk Nickel has teamed up with Russian Platinum to invest $4.4bn to develop mining projects in Siberia, which contains some of the world’s richest deposits of platinum and palladium. The two companies will form a joint venture to develop projects in the Taimyr Peninsula in Russia’s far north with an aim to become the world’s largest producer of the precious metals, they said Wednesday.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s certainly not obvious to me how this story is connected to Samsung. The
question at hand is to determine whether this news event is related to Samsung
and, if so, the nature of that relation so we can determine whether or not to
pass this event to our customer. We begin by constructing small graphs around
each of the entities. With these graphs in hand, we then compute a path given
Dijkstra’s algorithm between each of the marked endpoints. An example of such
a path is given in Figure 4.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://thomasmarkovich.com/assets/ir1.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
&lt;strong&gt;Figure 4:&lt;/strong&gt; Visualized excerpt from the Knowledge Graph that pertains to the relationship
between the Norilsk platinum group metals mine in Siberia, Russia and Samsung.
&lt;/p&gt;

&lt;p&gt;What we see in Figure 4 is that the knowledge graph believes that Iridium is a
Platinum Group Metal, and that Platinum Group Metals are mined in Norilsk. We
also see that the Knowledge Graph believes that Iridium is used in Organic
Light Emitting Diodes (or OLEDs), which just happen to be used in Samsung
phones. Therefore, this news event is likely relevant to our customer. In fact,
this event is highly relevant to our customer’s interest in Samsung because
Iridium is incredibly important to the production of OLED screens due to its
ability to make a blue LED. Indeed, Samsung has even funded researchers at MIT
and Harvard to explore alternatives to Iridium for OLED screens.&lt;/p&gt;

&lt;p&gt;This type of dependency analysis is illustrative of the power of a well formed
knowledge graph and it is critical for machine enabled semantic reasoning. It’s
easy to imagine this type of dependency analysis having uses not only in the
financial services industry, but also in work as wide ranging as supply chain
risk assessment and nuclear nonproliferation applications – just to name a few.&lt;/p&gt;

&lt;h2 id=&quot;graph-infrastructure&quot;&gt;Graph Infrastructure&lt;/h2&gt;
&lt;p&gt;In addition to standard graph features, we choose to endow each fact that is
stored in the knowledge graph with the time at which the edge was added and a
confidence for that edge. The time dependence intuitively follows from the
observation that the totality of human knowledge grows and changes over time.
Ultimately, this makes the graph dynamic, which is a natural feature of human
knowledge itself.&lt;/p&gt;

&lt;p&gt;There are a small number of facts that I’d be willing to bet my life on –
something like Auston Matthews is a Toronto Maple Leaf – and a great many facts
that I’d be willing to bet $20 dollars on – for example, the Boston Massacre
happened in 1770. Both are true but, due to the amount of information that I’ve
recently read, I know considerably more about the former than the latter and,
therefore, am more confident about it. Motivated by this, we have designed our
knowledge graph such that each edge has weights which we choose to interpret as
confidences. This data enables us to capture the inherent uncertainty necessary
to model a fast changing world and to reason about the validity of queries. By
virtue of the graph being probabilistic, we are able to embrace true Bayesian
reasoning as we attempt to evaluate a query, as well as provide query specific
priors to up or down weight an assertion based on the origin (e.g. a company’s
own statements about a new product release should be up-weighted over twitter
rumors).&lt;/p&gt;

&lt;p&gt;One of the most exciting engineering challenges of knowledge graphs is their
size. It is not uncommon to have a knowledge graph with more than 1 billion
facts and 50 million vertices; this can easily require hundreds of gigabytes of
RAM. Even more concerning than the memory requirements is the computational cost
of computing even basic graph properties such as the path length between
vertices. We have taken two complementary approaches to ensure that our graph
algorithms are as quick as possible. First, because our edges are interpreted
as probabilities, it is possible to set a probability cutoff beyond which we
are not interested in graph connections. This allows us to only consider graph
algorithms over highly restricted subsets of the graph, which provides us with
major algorithmic improvements. Second, we have engineered the data structure
to remain as cache coherent as possible by representing our knowledge graph as
a sparse three rank tensor in an attempt to optimize the per-fact throughput
through the CPU.&lt;/p&gt;

&lt;p&gt;We also have a clear route towards efficient parallelization by exploiting what
we are terming the “galactic structure” of the graph. While this is not a
general feature of all graphs, we have observed that there are highly connected
clusters of vertices that are only weakly connected to one another. Intuitively,
this makes sense. For example, consider domains such as the Toronto Maple Leafs
and modern particle physics – there is little overlap between these fields and
therefore no need to reason over a graph that contains both clusters of highly
interconnected vertices when reasoning about &lt;a href=&quot;https://en.wikipedia.org/wiki/Dave_Keon&quot;&gt;Dave Keon&lt;/a&gt;, the Toronto Maple Leafs
legend. This galactic structure provides us with a promising route towards
efficient parallelization using commodity hardware.&lt;/p&gt;

&lt;h2 id=&quot;where-are-we-going&quot;&gt;Where are We Going?&lt;/h2&gt;
&lt;p&gt;We’ve just started to teach the knowledge graph and show it how to perform basic
reasoning. The following are some of the many additional features that we are
adding that will ensure the accuracy, robustness, and efficiency of the graph
long into the future.&lt;/p&gt;

&lt;h3 id=&quot;probabilistic-reasoning&quot;&gt;Probabilistic Reasoning&lt;/h3&gt;
&lt;p&gt;Giving the knowledge graph the ability to reason probabilistically about the
validity of facts allows it to hold conflicting facts or hypotheses and
evaluate them later in the presence of more evidence. This can additionally be
used to evaluate nuance of queries. This can be achieved by using techniques
such as softening the axiomatic constraints that power the machine reasoning
engine and building ontology-specific Bayesian models. We anticipate that using
these techniques should make our knowledge graph more resilient to internal
errors.&lt;/p&gt;

&lt;h3 id=&quot;automatic-fact-checking&quot;&gt;Automatic Fact Checking&lt;/h3&gt;
&lt;p&gt;Of course, if we have a collection of facts that we intend to use as our
internal source of truth to augment business data, we should ensure that this
set of facts is correct. With our current knowledge graph size, we can perform
this fact checking using a mix of manual spot checking and axiomatic constraint
testing (e.g. a person can only be born in one country). This is the standard
technique for evaluating the correctness of knowledge graphs. As with most
machine learning tasks, this is incredibly person intensive and, therefore,
expensive. Additionally, it’s difficult to scale this technique to large graphs.
To address these issues, we’re excited to explore techniques related to
hinge-loss Markov random fields that are directionally aware. In addition to
being efficient, this allows us to look at a fact such as “Florida namedAfter
Flo Rida” and swap the directionality, instead of having to first infer that
we need to delete this edge and then infer that the reverse edge should be
present.&lt;/p&gt;

&lt;h3 id=&quot;automatic-graph-enrichment&quot;&gt;Automatic Graph Enrichment&lt;/h3&gt;
&lt;p&gt;Because it’s simply not possible to have humans continually teach the
knowledge graph, our system is being constructed to be capable of learning
facts on its own. There are many ways to do this including: tracking
unexplained queries, generalizing local and global graph features to infer new
facts from patterns, and using semantic information. Intuitively, this might
look like finding patterns such as “Companies tend to have a CEO” and one of
the companies in our graph does not currently have a CEO. Therefore, we should
enrich this region of the graph specifically relating to the specific company
and the existence of the CEO. To achieve this, we are actively exploring
modifications of techniques such as the path rank algorithm and graph embedding
methods as well as information retrieval techniques from the internet and other
sources. This is proving to be an exciting path of inquiry.&lt;/p&gt;

&lt;h3 id=&quot;graph-dynamics&quot;&gt;Graph Dynamics&lt;/h3&gt;
&lt;p&gt;Modeling the influence of specific edges on the connectivity of two marked
vertices in a graph is fundamental to understanding network resilience. In the
context of a knowledge graph, this provides us with information about the
influence of this fact. Intuitively, if we imagine that the vertices in our
graph are cities and the edges roads, with the edge weights corresponding to
the width of those roads (e.g. 0.1 is a one lane road and 1.0 is a 6 lane super
highway), then the time to travel between two different cities indicates the
strength of their connection. With many alternative routes and many wide
highways, we can say that those cities are tightly connected. Mathematically,
the problem can be thought about in terms of a two point correlation function
for a collection of random walks over the graph. These are discrete random
walks whose dynamics can be modeled with a discrete Green’s function. By taking
advantage of the connection between discrete Green’s functions on a graph of
random topology and discrete Laplace equations, we’ve preliminarily found that
it is possible to evaluate the influence of changing an edge. We’re excited to
formalize and harden this connection and expose these measures to aid in
producing more advanced models.&lt;/p&gt;

&lt;p&gt;The knowledge graph at Forge.AI is a crucial element of our technology stack
and it has exciting potential for further development. We look forward to
sharing with you further insights in the coming months.&lt;/p&gt;

&lt;p&gt;Note: This post was originally published on the Forge.AI blog:
&lt;a href=&quot;https://www.forge.ai/blog/knowledge-graphs-for-enhanced-machine-reasoning-at-forge.ai&quot;&gt;https://www.forge.ai/blog/knowledge-graphs-for-enhanced-machine-reasoning-at-forge.ai&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 14 Mar 2018 00:00:00 +0000</pubDate>
        <link>https://thomasmarkovich.com/articles/2018-03/knowledge-graphs</link>
        <guid isPermaLink="true">https://thomasmarkovich.com/articles/2018-03/knowledge-graphs</guid>
        
        
        <category>engineering</category>
        
      </item>
    
      <item>
        <title>Linking Python to C with CFFI</title>
        <description>&lt;h2 id=&quot;why-link-python-to-c&quot;&gt;Why link Python to C?&lt;/h2&gt;
&lt;p&gt;I hope that it is uncontroversial to state that Python is a great language that can suffer from occasional performance issues. This is especially true if Python is being used in heavy numerical computing environments like those at &lt;a href=&quot;https://www.gamalon.com&quot;&gt;Gamalon&lt;/a&gt;. Gamalon is not the first to require using Python for numerical tasks. To meet this need, libraries like &lt;a href=&quot;http://www.numpy.org&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NumPy&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.scipy.org&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SciPy&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;http://pandas.pydata.org&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pandas&lt;/code&gt;&lt;/a&gt; and others provide users with well tested implementations of most common numerical tasks. Most of these numerical tasks, such as &lt;a href=&quot;http://www.openblas.net&quot;&gt;matrix multiplication&lt;/a&gt; or &lt;a href=&quot;http://www.netlib.org/cephes/&quot;&gt;special function&lt;/a&gt; evaluation among others, have reference implementations in C or Fortran that are linked to Python through many layers of indirection.&lt;/p&gt;

&lt;p&gt;For rapid prototyping, this turns out to be a significant time saver, but what are the costs of these indirection layers? This can be answered by exploring the callgraph for the following code that simply evaluates the log of the probability density function of the Beta distribution with distributional parameters 1 and 2:&lt;/p&gt;

&lt;!--more--&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;scipy.stats&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This simple script has the following callgraph (click to zoom in):&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://svbtleusercontent.com/89mhz7habag0aq.jpg&quot;&gt;&lt;img src=&quot;https://svbtleusercontent.com/89mhz7habag0aq_small.jpg&quot; alt=&quot;callgraph_beta_logpdf.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We clearly see from the callgraph that a significant portion of our compute time is spent manipulating array shape, constructing arrays, and performing other associated operations. In fact, the underlying math for the distribution doesn’t even make an appearance in the callgraph!&lt;/p&gt;

&lt;p&gt;The story is certainly different if, instead of making repeated calls to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logpdf&lt;/code&gt; function with a scalar, we make a single call with the vector. In this situation, the call overhead of the array manipulation is swamped by the vectorized cost of the math itself. Overall runtime is reflected by:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Scalar&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Vector&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;45.7 s&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0.147 s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Vectorization, then, is the key to writing performant &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NumPy&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SciPy&lt;/code&gt; based code.&lt;/p&gt;

&lt;p&gt;Unfortunately, our specific use-cases typically involve lots of scalar calls, and thus, we encountered significant overhead. Can this cost be optimized away?&lt;/p&gt;

&lt;p&gt;Beyond just the overhead, what happens when the user wants to use PyPy or some other tracing JIT to optimize the rest of their Python code (e.g. dictionary operations and the like)? Many of these libraries are simply not compatible with PyPy given PyPy’s &lt;a href=&quot;http://pypy.org/compat.html&quot;&gt;partial support of the C API&lt;/a&gt;. In fact, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NumPy&lt;/code&gt; only recently passed all upstream tests when run under the PyPy interpreter, but PyPy was generally unable to provide &lt;a href=&quot;https://morepypy.blogspot.com/2015/02/linalg-support-in-pypynumpy.html&quot;&gt;any performance optimizations&lt;/a&gt;. Is it possible to still take advantage of the C and Fortran reference implementations in PyPy without rewriting them in Python?&lt;/p&gt;

&lt;p&gt;Generally yes. This linking can be done with a tool called &lt;a href=&quot;http://cffi.readthedocs.io/en/latest/&quot;&gt;CFFI&lt;/a&gt;, or the C Foreign Function Interface, which provides light weight, PyPy compatible, Python bindings for C code. In the remainder, we will explore how to write the bindings, how to link those bindings to Python, and what their associated performance impacts are.&lt;/p&gt;

&lt;h2 id=&quot;writing-and-building-the-c&quot;&gt;Writing and Building the C&lt;/h2&gt;
&lt;p&gt;For the sake of comparison, we will implement the Beta &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logpdf&lt;/code&gt; function in C. This function is given by:&lt;/p&gt;

\[logpdf(x; \alpha, \beta) = \log\left(\Gamma(\alpha + \beta)\right) - \log\left(\Gamma(\alpha)\right) - \log\left(\Gamma{\beta}\right) + (\alpha - 1) \log(x) + (\beta - 1) \log(1 - x)\]

&lt;p&gt;which can be implemented in C like so:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;beta_logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefactor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;prefactor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lgamma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lgamma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lgamma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefactor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we can then start to explore writing a CFFI compatible build script. The first place to start is by constructing the associated header file, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beta.h&lt;/code&gt;, for the above function. The entire contents of the header file are given by:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;beta_logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This looks just like a normal header file, because it is. This header file is used to tell CFFI which functions you want to link to Python. Therefore, you should only add function prototypes for the functions you actually want to link.&lt;/p&gt;

&lt;p&gt;With the header file and implementation in hand, we next turn our attention to implementing the build script that links with the API-level out-of-line linking paradigm. Generally, the CFFI works by taking the desired C code, automatically generating a C extension for the code, and then building that.&lt;/p&gt;

&lt;p&gt;The script begins by import &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CFFI&lt;/code&gt; and creating a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffibuilder&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffibuilder&lt;/code&gt; is the object that will take care of the code generation and compilation.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;cffi&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ffibuilder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cffi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FFI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffibuilder&lt;/code&gt; defined, we need to tell the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffibuilder&lt;/code&gt; where to find the file containing the method prototype for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beta_logpdf&lt;/code&gt; and its implementation.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sourcefile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;beta.h&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;beta.c&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we need to read in our files and build them. We first read in the header file and use the header file to inform the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_source&lt;/code&gt; method which functions to import. We also need to provide any necessary extra information about how to perform the code generation and provide any extra compilation arguments. Because our example is plain, we only need to tell the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffibuilder&lt;/code&gt; what to name the resulting module, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_beta&lt;/code&gt; in our case, where to find the module and sources, and how to compile it. While not expressly necessary, I try and compile with ‘-O3’, ‘-march=native’, ‘-ffast-math’ whenever it provides performance benefits. In this case, the move from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-O2&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-O3&lt;/code&gt; halves the run time.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourcefile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ffibuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ffibuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;_beta&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;#include &quot;{0}&quot;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourcefile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;library_dirs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;extra_compile_args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;-O3&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;-march=native&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;-ffast-math&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we build it. I prefer to build with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;verbose=True&lt;/code&gt; to ensure that I have all the information necessary to fix any problems that could occur.&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ffibuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Putting this all together, we have:&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;cffi&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ffibuilder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cffi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FFI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sourcefile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;beta.h&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;beta.c&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourcefile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ffibuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cdef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ffibuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;_beta&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&apos;#include &quot;{0}&quot;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourcefile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;library_dirs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;extra_compile_args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;-O3&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;-march=native&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;-ffast-math&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ffibuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;which, when run, reports a successful build. The successful build gives us multiple files; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_beta.c&lt;/code&gt; which is the emitted C extension module, the compiled object library, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_beta.o&lt;/code&gt;, and ` _beta.cpython-36m-darwin.so` which is the dynamic library that Python will actually load.&lt;/p&gt;

&lt;p&gt;As a closing remark, it is possible to define the C inline as described in the &lt;a href=&quot;http://cffi.readthedocs.io/en/latest/overview.html&quot;&gt;CFFI documentation&lt;/a&gt;, but I have found that it gets tedious to do this for large C projects, like those that we use internally.&lt;/p&gt;

&lt;h2 id=&quot;linking-with-python&quot;&gt;Linking with Python&lt;/h2&gt;
&lt;p&gt;Given the built module above, we can now import it into Python. The module will have the name prescribed by the first argument to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_source&lt;/code&gt; method, which in our case was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_beta&lt;/code&gt;. Because we’re only interested in the library itself and not any of the other &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FFI&lt;/code&gt; periphery, such as memory allocation, we only need to import &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib&lt;/code&gt;. Importing only the function that we actually used, our import statement is simply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;from _beta.lib import beta_logpdf&lt;/code&gt;. Note, the name of the function in C is the same as that in the module.&lt;/p&gt;

&lt;p&gt;Putting this all together, we can call the linked function with:&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;_beta.lib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta_logpdf&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;beta_logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;performance-tests&quot;&gt;Performance Tests&lt;/h2&gt;
&lt;p&gt;Running the scripts detailed in the Scripts section below, we see the following results.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Method&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Time [s]&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Slowdown (C Reference)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Reference C&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0.0302 s&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SciPy&lt;/code&gt; Scalar&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;45.7 s&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1510&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SciPy&lt;/code&gt; Vector&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0.147 s&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;4.87&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CFFI&lt;/code&gt; in cPython 3.6&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0.346 s&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;11.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CFFI&lt;/code&gt; in PyPy3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0.0449 s&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1.49&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Where all timings were generated on my Mid-2017 Non-touchbar MacBook Pro with 2.3 GHz Intel Core i5. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SciPy&lt;/code&gt; scalar and vector results are exactly the same as those reported in the introduction. The reference C results were computed by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beta_logpdf&lt;/code&gt; function reported above and timed using the system clock available in the C standard library. The C reference was compiled with GCC 7 with compiler flags &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-O3&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-march=native&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ffast-math&lt;/code&gt;. The reported CFFI numbers were computed using the exact build scripts linked above. We elected to test in both cPython 3.6 and PyPy3 to test the possible benefits that a tracing JIT can afford us in super simplistic scenarios like this. cPython 3.6 and PyPy3 are both from the bottle poured with Homebrew.&lt;/p&gt;

&lt;p&gt;Interestingly enough, we observe that CFFI provides two order of magnitude improvement in the overhead for calling the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logpdf&lt;/code&gt; function within cPython. This is already a drastic speedup, but we can do even better by running under PyPy, which provides only a factor of 2 slow-down versus the reference C.&lt;/p&gt;

&lt;p&gt;What is the source of the remaining factor of two? We can explore the computational costs of an exceedingly simple function to further understand the overhead cost of the interpreter. To understand this, we will time a function that takes in three values and returns one:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;nop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Linking in the method described above and then timing under both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyPy3&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cPython 3.6&lt;/code&gt;, observe the following timings:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Interpreter&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Time [s]&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Slowdown (PyPy Reference)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;cPython 3.6&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0.248 s&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;15.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;PyPy3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;0.0164 s&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Interestingly enough, that means that, calling an empty function linked with CFFI from cPython costs on average 248 ns, while the equivalent tasks costs 16 ns in PyPy. We can then see that the majority of the computational benefit that we have observed from moving from cPython to PyPy is the more efficient C interface.&lt;/p&gt;

&lt;h2 id=&quot;closing-remarks&quot;&gt;Closing Remarks&lt;/h2&gt;
&lt;p&gt;Motivated by Knuth’s oft-cited adage that “premature optimization is the root of all evil”, the general advice that is given in these situations is to write working code, profile it, and then optimize hotspots. For Python, one of the common approaches to such an optimization is to translate the hotspots to C and then link them to Python.&lt;/p&gt;

&lt;p&gt;In the above, we have explored specific techniques for such an optimization pathway that also permits the user to use interpreters such as PyPy. This allows for using a JIT to optimize other aspects of the codebase, such as heavy dictionary access, or string computations.&lt;/p&gt;

&lt;p&gt;Combining &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyPy&lt;/code&gt; with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CFFI&lt;/code&gt; linked module, it is possible to obtain performance within an order of magnitude to C while still operating within Python. Amazingly enough, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyPy&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CFFI&lt;/code&gt; out performs even vectorized &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SciPy&lt;/code&gt; in this situation! Granted, this is a sample size of 1 but it is at least encouraging.&lt;/p&gt;

&lt;p&gt;Of course, CFFI is not a magic bullet. Performing optimizations of this kind can significantly limit the flexibility of certain aspects of your code-base, and require developers to be more deliberate about their use of interfaces. It also adds maintenance costs because developers will now need to be able to support both C and Python.&lt;/p&gt;

&lt;h4 id=&quot;edits&quot;&gt;Edits&lt;/h4&gt;
&lt;p&gt;Note: The timings were updated to reflect an updated version of the underlying C function that matches the scipy behaviour better. Thank you @mgeier for pointing out this error!&lt;/p&gt;

&lt;h2 id=&quot;scripts&quot;&gt;Scripts&lt;/h2&gt;
&lt;p&gt;The callgraph was generated with&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python -m cProfile -o prof.out beta.py
gprof2dot.py -f pstats prof.out | dot -Tsvg -o callgraph.svg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cPython 3.6&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;_beta.lib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta_logpdf&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;scipy.stats&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta_logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run only under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PyPy3&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;_beta.lib&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta_logpdf&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beta_logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compiled with GCC 7 with command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcc -O3 -march=native -ffast-math -finline-functions beta.c -lm&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;math.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;time.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;nop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;xlogy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;beta_logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefactor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;prefactor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lgamma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lgamma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lgamma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefactor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xlogy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xlogy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;clock_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;beta_logpdf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CLOCKS_PER_SEC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Time in seconds %f&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Sat, 19 Aug 2017 00:00:00 +0000</pubDate>
        <link>https://thomasmarkovich.com/articles/2017-08/linking-python-to-c-with-cffi</link>
        <guid isPermaLink="true">https://thomasmarkovich.com/articles/2017-08/linking-python-to-c-with-cffi</guid>
        
        
        <category>engineering</category>
        
      </item>
    
  </channel>
</rss>
