Questions concerning circular reference / self-referencing in types, specifically regarding performance

211 views
Skip to first unread message

Christopher Alexander

unread,
Dec 31, 2015, 5:42:11 PM12/31/15
to julia-users
Hello all, 

I have a question about the usage of circular references in types.  In a project I am working on, I am running into several situations where this type of design appears to be the best issue.  For example, let's say I have this (this is a massive simplification but illustrates my point):

abstract TermStructure


type
Bond
rate
::Float64
ts
::TermStructure
mat_date
::Date
end


function Bond(rate::Float64, mat_date::Date)
   ts
= NullTermStructure()
   
return Bond(rate, ts, mat_date)
end


type
NullTermStructure <: TermStructure end


type
PiecewiseYieldCurve <: TermStructure
settlement
::Date
bonds
::Vector{Bond}
end


function PiecewiseYieldCurve(settlement::Date, sched::Schedule, rate::Float64)
  bonds
= Vector{Bond}(length(sched))
 
for (i, d) in enumerate(sched)
     new_bond
= Bond(rate, d)
     bonds
[i] = new_bond
 
end
 
  pyc
= PiecewiseYieldCurve(settlement, bonds)
 
for b in bonds
    b
.ts = pyc
 
end
 
 
return pyc
end


Firstly, I guess, is this the best implementation?  There are situations where I need the bonds of the PiecewiseYieldCurve to be aware of the TermStructure to which they are connected.  Secondly, do I take a performance hit from this?  The PiecewiseYieldCurve instance will have parts of it updated as I run some of my pricer methods (not shown).

Thanks!

Chris

Tim Holy

unread,
Dec 31, 2015, 6:33:29 PM12/31/15
to julia...@googlegroups.com
See http://docs.julialang.org/en/stable/manual/faq/#how-do-abstract-or-ambiguous-fields-in-types-interact-with-the-compiler and the section
afterwards. You'd be better off with

type Bond{T<:TermStructure}
rate::Float64
ts::T
mat_dat::Date
end

--Tim

Christopher Alexander

unread,
Jan 1, 2016, 1:24:21 PM1/1/16
to julia-users
Thanks Tim, this is very helpful!  Happy New Year!

- Chris
Message has been deleted

Christopher Alexander

unread,
Jan 1, 2016, 2:01:02 PM1/1/16
to julia-users
I'm actually having a slight problem with this implementation.  Basically, in using the parametric approach above, when I initially create an instance of the Bond type with the NullTermStructure and then set its term structure with the new TermStructure instance that I create (creating the circular reference), I get a convert error.  I'm assuming this is because the initial instantiation of the Bond wants a TermStructure of type NullTermStructure, so then when I try to re-set it with PiecewiseYieldCurve, it complains.  Is there any way to avoid setting it with the NullTermStructure in the first place?

Thanks!

Chris

Tim Holy

unread,
Jan 1, 2016, 4:45:12 PM1/1/16
to julia...@googlegroups.com
If you make `ts` the last parameter, using an inner constructor you can leave
that field undefined when you initially create the object, then fill it in later.
You may need to sprinkle some `isdefined(b, :ts)` in various places in your
code.

Happy New Year!
--Tim

Christopher Alexander

unread,
Jan 1, 2016, 5:06:33 PM1/1/16
to julia-users
Thanks!  I'm having a bit of trouble with that setup when the type is parameterized, as below:

abstract AbstractA


type A <: AbstractA end


type B{T <: AbstractA}

 a::T


  B() = new()

end


I am getting this error:

ERROR: MethodError: `convert` has no method matching convert(::Type{B{T<:AbstractA}})


This may have arisen from a call to the constructor B{T<:AbstractA}(...),


since type constructors fall back to convert methods.


Closest candidates are:


  convert{T}(::Type{T}, ::T)


  call{T}(::Type{T}, ::Any)


 in call at essentials.jl:57


Is that the proper way to set that up?

Thanks!!

Chris

Christopher Alexander

unread,
Jan 1, 2016, 5:24:13 PM1/1/16
to julia-users
OK, I've gotten by this by doing:

B{AbstractA}()


... but it would be neat if I could just infer that type in the inner constructor.

Christopher Alexander

unread,
Jan 1, 2016, 7:30:35 PM1/1/16
to julia-users
OK, now I have it:

type DiscountingBondEngine{Y <: YieldTermStructure} <: PricingEngine{Y}
  yts::Y

  function call(::Type{DiscountingBondEngine})
    new{YieldTermStructure}()
  end

  function call{Y}(::Type{DiscountingBondEngine}, yts::Y)
    new{Y}(yts)
  end
end

This let's you call something like this with no issues:
pricing_engine = DiscountingBondEngine()

Then you can set the term structure later.

Julia is awesome.

Thanks!

Chris
Reply all
Reply to author
Forward
0 new messages