# API design help: Wrapping ITensorMPS structs and extending it to my package

**URL:** <https://discourse.julialang.org/t/api-design-help-wrapping-itensormps-structs-and-extending-it-to-my-package/139164>\
**Category:** General Usage\
**Tags:** question, package, itensors, package-development\
**Created:** [September 2, 2026, 1:38pm UTC](https://discourse.julialang.org/t/api-design-help-wrapping-itensormps-structs-and-extending-it-to-my-package/139164 "2026-09-02T13:38:09Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Gautameshwar](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gautameshwar/32/220531_2.png) [@Gautameshwar](https://discourse.julialang.org/u/Gautameshwar)\
**Post date:** [September 2, 2026, 1:38pm UTC](https://discourse.julialang.org/t/api-design-help-wrapping-itensormps-structs-and-extending-it-to-my-package/139164/1 "2026-09-02T13:38:09Z")

</div>

Hi Julia community!

I am currently developing a package, [ProcessTensors.jl](https://github.com/Gauthameshwar/ProcessTensors.jl), that uses an MPS/MPO architecture to construct process tensors and simulate non-Markovian quantum dynamics. I built this package on top of the features offered in [ITensors.jl](https://github.com/ITensor/ITensors.jl) and [ITensorMPS.jl](https://github.com/ITensor/ITensorMPS.jl). Since I am a big fan of these two packages and find their user interface appealing (thank you, @mtfishman!), I wanted to have the same API experience in my package as well. However, I also need to add some more abstraction on top of whatever is currently offered in ITensors so that they can be used by my package to handle problems tailored to non-Markovianity and open-quantum systems.

In particular, I have to distinguish between Hilbert space (computational basis space) and Liouville space (operator basis space) and store metadata that allows us to move between the two spaces. This made me wrap the existing `MPS`/`MPO` structs in my own package’s abstractions:

```julia-auto
abstract type AbstractMPS{S <: AbstractSpace} <:
    ITensorMPS.AbstractMPS end

struct MPS{S <: AbstractSpace, C} <: AbstractMPS{S}
    core::ITensorMPS.MPS
    combiners::C
end

```

Here, for example, `MPS{Hilbert}` represents an ordinary state, whereas `MPS{Liouville}` represents a vectorized density operator and stores the combiners needed to reconstruct its Hilbert-space MPO. Along with this, I also wanted the existing functions of ITensorMPS to be available for users to be used the same way in my package as you would in the former. So I had to export functions such as `siteinds`, `linkinds`, `OpSum`, `apply`, and `tdvp` in my package so that users can use them without worrying about the wrappers I introduce in my package.

After having some conversation with ChatGPT, I went with a strategy which, at that time, felt solid:

1. Have certain functions import and re-export directly from ITensorMPS. Functions like `OpSum` and `op` fall under this category.

2. Have inspection functions such as `siteinds`, `linkinds` extend the same ITensorMPS generic and delegate to the wrapped core.

3. Finally, for functions that return an `MPS`/`MPO` object, I rewrap the same object in my package’s convention after forwarding the calculations to `ITensorMPS`. For example, in `tdvp` function that returns a time-evolved MPS, given an input and a Hamiltonian,

This gives users the familiar ITensorMPS function names while allowing operations on `MPS{Hilbert}` and `MPS{Liouville}`.

Now, I am planning to register the package in General, and I wish to keep it such that I (and others willing to contribute) can develop it comfortably in the future. So I want to hear from the experts here on whether this is a robust way of extending an existing software package’s features and building upon it to customize it to my specific set of problems, while keeping the existing package’s features intact while doing so. I am not aware of any better way of doing this, so if you guys have any comments or instances where this could go wrong, and a workaround for that, I’d be grateful for receiving your advice/suggestions 🙏🏻.

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [September 3, 2026, 10:26pm UTC](https://discourse.julialang.org/t/api-design-help-wrapping-itensormps-structs-and-extending-it-to-my-package/139164/2 "2026-09-03T22:26:50Z")

</div>

I think this sounds like exactly the right way to go about it!

Is the general goal also, that your `ProcessTensor`s can be used in place of ITensors MPS / MPOs? Then extending the ITensor interfaces to your own types is exactly the way to make generic code work on both ITensors MPSs / MPOs and your ProcessTensors.

Two smaller notes:

```julia-auto
abstract type AbstractMPS{S <: AbstractSpace} <:
    ITensorMPS.AbstractMPS end

struct MPS{S <: AbstractSpace, C} <: AbstractMPS{S}
    core::ITensorMPS.MPS
    combiners::C
end

```

is maybe a bit confusing, because the names `AbstractMPS` and `MPS` are already taken in `ITensor`s. how about `AbstractProcessMPS` and `ProcessMPS`?

And if you have a lot of code that just forwards to applying the same function to the `core` you can reduce the boilerplate using the @eval macro:

```julia-auto
for f in [:siteinds, :linkinds]
    @eval $f(s::AbstractMPS, args...; kwargs...) = $f(s.core, args...; kwargs...)
end

```

---

<div class="post-metadata">

**Author:** ![Gautameshwar](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gautameshwar/32/220531_2.png) [@Gautameshwar](https://discourse.julialang.org/u/Gautameshwar)\
**Post date:** [September 4, 2026, 2:36am UTC](https://discourse.julialang.org/t/api-design-help-wrapping-itensormps-structs-and-extending-it-to-my-package/139164/3 "2026-09-04T02:36:12Z")

</div>

Thank you very much for your comments @jlbosse!

> [@jlbosse](#):
>
> Is the general goal also, that your `ProcessTensor`s can be used in place of ITensors MPS / MPOs?

Yes. I want my process tensors package to be used without manually importing ITensorMPS MPS, and MPOs, and also have my own features on top of it.

> [@jlbosse](#):
>
> how about `AbstractProcessMPS` and `ProcessMPS`?

Good that you brought this up. Initially, I was playing with

```julia-auto
abstract type AbstractMyMPS{S <: AbstractSpace} <: end

struct MyMPS{S <: AbstractSpace, C} <: AbstractMyMPS{S}
    core::MPS
    combiners::C
end

```

and avoiding the confusion of redundant names. But then later, when I started using my own package to define some problem, I realised they needed to be changed to a more appropriate name 😅. `ProcessMPS` and `ProcessMPO` are not a good choice because the word “process” is an established term in the open quantum systems community. So a process has some inherent properties that ordinary MPS/MPOs do not. So it might confuse users when they define a `ProcessMPS(s, ["Dn"])` when they simply want to define an ordinary `MPS` that has a down spin at one site.

I thought about some good names for the MPS and MPO that I could define and ship, but I felt keeping it the same as `ITensorMPS` would keep the user experience seamless in adapting to my API. The downside is, of course, that I have some slightly confusing definitions in my source code 🥲.
