Home > Back-end >  When declaring static variable for conformance to AdditiveArithmetic, cannot call instance member fr
When declaring static variable for conformance to AdditiveArithmetic, cannot call instance member fr

Time:07-24

I know this sounds crazy for a 10-year-old, but because S4TF doesn't work for me, I'm building my own neural network library in Swift. (I haven't gotten that far.) I'm creating a structure that conforms to AdditiveArithmetic. It also uses Philip Turner's Differentiable, but that's unimportant.

Anyway, when defining the zero variable, I call another variable dimen, defined in the same structure. This raises an error: instance member 'dimen' cannot be used on type 'Electron<T>'

note: the structure I am creating is going to be used to create a multi-dimensional array for neural networks.

Total code (stripped down to remove unimportant bits):

public struct Electron<T> where T: ExpressibleByFloatLiteral, T: AdditiveArithmetic {
    var energy: [[Int]: T] = [:]
    var dimen: [Int]
    public init(_ dim: [Int], with: ElectronInitializer) {
        self.dimen = dim
        self.energy = [:]
        var curlay = [Int](repeating: 0, count: dimen.count)
        curlay[curlay.count-1] = -1
        while true {
            var max: Int = -1
            for x in 0..<curlay.count {
                if curlay[curlay.count-1-x] == dimen[curlay.count-1-x]-1 {
                    max = curlay.count-1-x
                }
                else {break}
            }
            if max == 0 {break}
            else if max != -1 {
                for n in max..<curlay.count {
                    curlay[n] = -1
                }
                curlay[max-1]  = 1
            }
            curlay[curlay.count-1]  = 1
            print(curlay)
            energy[curlay] = { () -> T in
                switch with {
                    case ElectronInitializer.repeating(let value):
                    return value as! T
                    case ElectronInitializer.random(let minimum, let maximum):
                    return Double.random(in: minimum..<maximum) as! T
                }
            }()
        }
    }
    subscript(_ coordinate: Int...) -> T {
        var convertList: [Int] = []
        for conversion in coordinate {
            convertList.append(conversion)
        }
        return self.energy[convertList]!
    }
    public mutating func setQuantum(_ replace: T, at: [Int]) {
        self.energy[at]! = replace
    }
}
extension Electron: AdditiveArithmetic {
    public static func - (lhs: Electron<T>, rhs: Electron<T>) -> Electron<T> where T: AdditiveArithmetic, T: ExpressibleByFloatLiteral {
        var output: Electron<T> = lhs
        for value in lhs.energy {
            output.energy[value.key] = output.energy[value.key]!-rhs.energy[value.key]!
        }
        return output
    }

    public static var zero: Electron<T> {
        return Electron.init(dimen, with: ElectronInitializer.repeating(0.0))
    }

    static prefix func   (x: Electron) -> Electron {
        return x
    }
    public static func   (lhs: Electron<T>, rhs: Electron<T>) -> Electron<T> where T: AdditiveArithmetic, T: ExpressibleByFloatLiteral {
        var output: Electron<T> = lhs
        for value in lhs.energy {
            output.energy[value.key] = output.energy[value.key]! rhs.energy[value.key]!
        }
        return output
    }
}
public enum ElectronInitializer {
    case random(Double, Double)
    case repeating(Double)
}

Error:

NeuralNetwork.xcodeproj:59:30: error: instance member 'dimen' cannot be used on type 'Electron' return Electron.init(dimen, with: ElectronInitializer.repeating(0.0))

I don't know what's happening, but thanks in advance. I'm new to Stack Overflow, so sorry if I did something wrong.

CodePudding user response:

The root of the problem is that dimen is an instance property, while zero is a static property. In a static context, you don't have an instance from which to access dimen, and so the compiler gives you the error. static properties and methods are a lot like global variables and free-functions with respect to accessing instance properties and methods. You'd have to make an instance available somehow. For a static function, you could pass it in, but for a static computed property, you'd either have to store an instance in a stored static property, which isn't allowed for generics, or you'd have to store it in a global variable, which isn't good either, and would be tricky to make work for all the possible T.

There are ways to do what you need though. They all involve implementing some special behavior for a zero Electron rather than relying on access to an instance property in your static .zero implementation. I made some off-the-cuff suggestions in comments, which would work; however, I think a more elegant solution is to solve the problem by creating a custom type for energy, which would require very few changes to your existing code. Specifically you could make an Energy type nested in your Electron type:

    internal struct Energy: Equatable, Sequence {
        public typealias Value = T
        public typealias Key = [Int]
        public typealias Element = (key: Key, value: Value)
        public typealias Storage = [Key: Value]
        public typealias Iterator = Storage.Iterator
        public typealias Keys = Storage.Keys
        public typealias Values = Storage.Values

        private var storage = Storage()
        
        public var keys: Keys { storage.keys }
        public var values: Values { storage.values }
        public var count: Int { storage.count }

        public init() { }
        
        public subscript (key: Key) -> Value? {
            get { storage.isEmpty ? .zero : storage[key] }
            set { storage[key] = newValue }
        }

        public func makeIterator() -> Iterator {
            storage.makeIterator()
        }
    }

The idea here is that when energy.storage is empty, it returns 0 for any key, which allows you to use it as a .zero value. I've made it internal, because energy defaults to internal, and so I've done a minimalist job of wrapping a Dictionary, mainly providing subscript operator, and making it conform to Sequence, which is all that is needed by code you provided.

The only changes needed in the rest of your code are to change the definition of energy

    var energy: Energy

Then to set it in your initializer, by-passing the bulk of your init when dim is empty.

    public init(_ dim: [Int], with: ElectronInitializer) {
        self.dimen = dim
        self.energy = Energy() // <- Initialize `energy`

        // Empty dim indicates a zero electron which doesn't need the 
        // rest of the initialization
        guard dim.count > 0 else { return }
        
        var curlay = [Int](repeating: 0, count: dimen.count)
        curlay[curlay.count-1] = -1
        while true {
            var max: Int = -1
            for x in 0..<curlay.count {
                if curlay[curlay.count-1-x] == dimen[curlay.count-1-x]-1 {
                    max = curlay.count-1-x
                }
                else {break}
            }
            if max == 0 {break}
            else if max != -1 {
                for n in max..<curlay.count {
                    curlay[n] = -1
                }
                curlay[max-1]  = 1
            }
            curlay[curlay.count-1]  = 1
            print(curlay)
            energy[curlay] = { () -> T in
                switch with {
                    case ElectronInitializer.repeating(let value):
                    return value as! T
                    case ElectronInitializer.random(let minimum, let maximum):
                    return Double.random(in: minimum..<maximum) as! T
                }
            }()
        }
    }

And then of course, to change how you create it in your zero property

    public static var zero: Electron<T> {
        return Electron.init([], with: ElectronInitializer.repeating(0.0))
    }

ElectronInitializer isn't actually used in this case. It's just a required parameter of your existing init. This suggests an opportunity to refactor initialization, so you could have an init() that creates a zero Electron in addition to your existing init(dim:with:)

  • Related